İçeriğe geç
Tüm yazılar

2026'da androidx.startup: bir yığın ContentProvider olmadan kütüphane başlatma sırası

Android'in App Startup kütüphanesine pratik bir bakış — initializer'ları tek bir ContentProvider'da toplamak, bağımlılıkları tanımlamak ve yerine geçemediği tembel-init durumları.

MFKAPPS 4 dk okuma

İlk Activity’niz çalışmadan önce kod çalıştırması gereken her kütüphane bunu aynı şekilde yapar: hiçbir sorgu mantığı olmayan, tek işi uygulama başlangıcında tetiklenmek olan bir ContentProvider. WorkManager bunu yapar. Firebase bunu yapar. Eklediğiniz herhangi bir analiz veya çökme raporlama SDK’sı da bunu yapar. Her biri kendi kodunuzda görünmezdir, ve her biri uygulamanız tek bir piksel bile çizmeden önce tam bir bileşen başlatması yapıyordur — binder kaydı, yeni bir ContentResolver çağrısı. androidx.startup, bunların hepsini kazara oluşmuş bir bağımlılık grafiği yerine açık bir tane ile tek bir ContentProvider’da toplamak için var.

Bunu, WorkManager’ın, küçük bir loglama katmanının ve kendi ön yükleme adımımın her birinin kendi provider’ını kaydettiği Mintly üzerinde soğuk başlatma süresini kısaltırken keşfettim. İşte App Startup’ın gerçekte size ne kazandırdığı ve manuel tembel başlatmanın hâlâ üstün geldiği yerler.

Çözdüğü problem

ContentProvider.onCreate(), manifestte tanımlı her provider için, Application.onCreate() geri dönmeden önce, ana thread üzerinde, Android’in garanti etmediği bir sırada çalışır. Bir tane olduğunda bu sorun değildir. Üç dört kütüphane her biri kendi provider’ını gönderdiğinde sorun olmaya başlar, çünkü:

  • Her provider, sistemin örneklemesi ve kaydetmesi gereken, kendi sabit yükü olan tam bir bileşendir.
  • Sıralama üzerinde kontrolünüz yok. Loglama katmanınızın WorkManager’ın Configuration’ının zaten var olmasına ihtiyacı varsa, bir sözleşme olmayan manifest birleştirme sırasına güveniyorsunuz demektir.
  • Bir build varyantı için birini kolayca devre dışı bırakamazsınız — çökme raporlamasına ihtiyaç duymayan bir debug build, yine de o provider’ın onCreate()’inin bedelini öder.

Provider örneklemenin sabit maliyetini build.gradle’ınızdaki her SDK ile çarpın, ve genelde korumaya çalıştığınız tam da o metrikte — soğuk başlatma — birikir.

App Startup bunun yerine ne koyuyor

androidx.startup tek bir InitializationProvider tanımlar, ve kütüphaneniz veya uygulamanız kendi provider’ını göndermek yerine buna Initializer<T> implementasyonları kaydeder. WorkManager’ın kendisi yıllar önce bu modele geçti — WorkManagerInitializer, ayrı bir provider yerine sizin kendi initializer’larınızın geçtiği aynı InitializationProvider üzerinden çalışır.

Küçük bir loglama kurulumu için bir initializer şöyle görünür:

class LoggingInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        Log.setLogLevel(if (BuildConfig.DEBUG) Log.VERBOSE else Log.WARN)
    }

    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

Manifestte, tek InitializationProvider birleştirme noktası altında bir kez tanımlanır:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.mfkapps.mintly.LoggingInitializer"
        android:value="androidx.startup" />
</provider>

App Startup kullanan her kütüphane, <meta-data> girişlerini manifest birleştirme yoluyla bu aynı provider bloğuna birleştirir. Sonunda, eskiden kütüphane başına bir tane gerektiren işi yapan tek bir ContentProvider örneğiniz olur.

Gerçek bağımlılıkları tanımlamak

Eskiden manifest sırası şansının örttüğü kısım dependencies()’tir. Bir oturum izleme initializer’ının önce WorkManager’ın yapılandırılmış olmasına ihtiyacı varsa, bunu açıkça söyleyin:

class SessionTrackingInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        WorkManager.getInstance(context).enqueue(sessionHeartbeatRequest())
    }

    override fun dependencies(): List<Class<out Initializer<*>>> =
        listOf(WorkManagerInitializer::class.java)
}

App Startup bu tanımlamalardan bir bağımlılık grafiği kurar ve topolojik olarak sıralar, böylece WorkManagerInitializer.create() her zaman SessionTrackingInitializer.create() çalışmadan önce biter — birleştirme sırasından bağımsız olarak, hangi Gradle modülünün hangi bağımlılığı önce tanımladığından bağımsız olarak. Bu garanti gerçek özelliktir; provider’ları tek bir yerde toplamak sadece bunu mümkün kılan mekanizmadır.

Kütüphaneyi silmeden birini kapatmak

Diğer yaygın ihtiyaç, belirli bir build varyantı için belirli bir initializer’ı devre dışı bırakmaktır — örneğin bir debug build’in üretim analiz hattını başlatmaması gerekir. Bağımlılığı çatallamazsınız; o varyantın manifestinde <meta-data> girişini kaldırırsınız:

<!-- src/debug/AndroidManifest.xml -->
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="merge">
    <meta-data
        android:name="com.mfkapps.mintly.analytics.AnalyticsInitializer"
        tools:node="remove" />
</provider>

Kütüphanenin kodu dokunulmadan kalır; sadece o varyant için başlangıçta onu çalıştıran tetikleyici gider.

Yardımcı olmadığı yerler

App Startup sadece bir provider’a sahip olmanın yükünü kaldırır. Initializer’ın create() metodunun gerçekte ne yaptığının yükü konusunda hiçbir şey yapmaz — ve create() varsayılan olarak hâlâ ana thread üzerinde eşzamanlı çalışır, tıpkı eski ContentProvider.onCreate() gibi. Bir SDK’nın başlatılmasının gerçekten ilk kareniz bitmeden tamamlanmaya ihtiyacı yoksa, App Startup çözüm değildir; bu işi Application.onCreate()’ten başlatılan bir arka plan coroutine’ine, ya da ilk kullanımda tembel olarak ertelemek çözümdür. Mintly’nin oturum izleme initializer’ını bir Initializer olmaktan tamamen çıkarıp, ilk kare çizildikten sonra tetiklenen tek seferlik bir WorkManager isteğine taşıdım — yakında olması gerekiyordu, her şeyden önce değil.

Vardığım kural şu: başka kodun güvenle dokunabilmesi için bir kütüphanenin önceden yapılandırması gereken her şey için androidx.startup kullanın — WorkManager’ın kendi durumu, ya da diğer initializer’ların var olduğunu varsaydığı bir bağımlılık enjeksiyon konteyneri gibi. Sadece erken başlatılması uygun olan her şey için ertelenmiş, tembel veya arka plan başlatması kullanın. Provider’ları tek yerde toplamak ve erken çalışması gerekenleri sıralamak gerçek bir değer; App Startup’ı her SDK’nın standart kodunu koyacağınız bir yer olarak görmek, aynı ana thread maliyetini birkaç yer yerine tek bir farklı provider’a taşımaktan başka bir şey değildir.