Ek açıklamaları kullanarak saklama kuralları oluşturma

Jetpack Keep, yansıtma ve Java Native Interface (JNI) kullanımını ayrı Keep kuralları dosyaları yerine doğrudan Kotlin veya Java kaynak kodunuzda ek açıklamalarla bildirmenize olanak tanır. Jetpack Keep Gradle eklentisi, bu ek açıklamaları derleme sırasında okur ve R8'in ihtiyaç duyduğu saklama kurallarını oluşturur.

Jetpack Keep, kod tabanınız değiştikçe R8 optimizasyonunun etkili kalmasına yardımcı olur:

  • Saklama kuralları, ilgili kodun yanında kalır. Yansıma kullanan kodu yeniden adlandırdığınızda veya sildiğinizde ek açıklamayı aynı yerde güncellersiniz. Senkronize edilecek ayrı bir kural dosyası yoktur.

  • Oluşturulan kurallar koşulludur. R8, yansıtıcı bir çağrının hedefini yalnızca çağrıyı yapan kodu da tutarsa korur. Çağrı kodu kullanılmıyorsa ve R8 bu kodu kaldırırsa (genellikle isteğe bağlı kitaplık özelliklerinde geçerlidir) R8, hedefi de kaldırabilir. Böylece, kullanılmayan özelliklerden gelen kurallar gereksiz yer kaplamaz.

  • Lint, yansımayı sizin için bulur. Kitaplık, açıklama eklenmemiş yansıtıcı çağrıları işaretleyen ve açıklamaların çoğunu otomatik olarak ekleyebilen lint kontrolleri içerir.

Jetpack Keep'in işleyiş şekli

Bir sınıfı ada göre örneklendiren bir işlev düşünün. R8, kodunuzun sınıfı kullandığını anlayamazsa sınıfın oluşturucusunu kaldırır ve uygulama çalışma zamanında çöker. Kilitlenmeyi önlemek için saklama kuralları dosyanıza aşağıdaki gibi bir kural ekleyebilirsiniz:

# Unconditional: R8 keeps this constructor even if it removes the reflection that invokes it.
-keep class com.example.ResourceLocatorImpl {
    <init>();
}

Jetpack Keep ile bunun yerine yansımayı gerçekleştiren işleve ek açıklama eklersiniz:

// ResourceLocatorImpl isn't on this module's compile classpath, so both the
// annotation and the code refer to it by name.
@UsesReflectionToConstruct(
    className = "com.example.ResourceLocatorImpl",
    // Omitting parameterTypes keeps every constructor. An empty array keeps
    // only the no-argument constructor, which the code calls.
    parameterTypes = [],
)
fun loadResourceLocator(): ResourceLocator {
    val implClass = Class.forName("com.example.ResourceLocatorImpl")
    return implClass.getDeclaredConstructor().newInstance() as ResourceLocator
}

Derleme sırasında Gradle eklentisi, kurucu için bir saklama kuralı oluşturur. Önceki manuel olarak yazılan kuralın aksine, oluşturulan kural koşulludur: R8, yalnızca loadResourceLocator optimize edilmiş uygulamada kalırsa oluşturucuyu tutar.

Şartlar

Jetpack Keep'i kullanmak için projenizde Android Gradle eklentisi (AGP) 8.7 veya sonraki bir sürümün yüklü olması gerekir.

Gradle eklentisi aşağıdaki modül türlerini destekler:

Modül türü Modülün uyguladığı eklenti Ek kurulum
Android uygulaması com.android.application Yok
Android kitaplığı com.android.library Yok
Android hedefli Kotlin Multiplatform kitaplığı com.android.kotlin.multiplatform.library Yok
JVM kitaplığı (Java veya Kotlin) java-library veya org.jetbrains.kotlin.jvm JAR dönüştürme işlemi kaydetme

Jetpack Keep'i kurma

Jetpack Keep'i kullanmak için hem kitaplığı hem de Gradle eklentisini yansıma veya JNI kullanan her modüle ekleyin. Kitaplık, ek açıklamaları ve lint kontrollerini sağlar. Eklenti ise ek açıklamaları saklama kurallarına dönüştürür.

  1. libs.versions.toml dosyanıza kitaplığı ve eklentiyi ekleyin. Kitaplık ve eklenti aynı sürümü paylaşır:

    [versions]
    # The library and the plugin ship together. Use one version for both so
    # that they stay in sync when you upgrade.
    annotationKeep = "1.0.0-alpha01"
    
    [libraries]
    androidx-annotation-keep = { group = "androidx.annotation.keep", name = "keep", version.ref = "annotationKeep" }
    
    [plugins]
    androidx-annotation-keep = { id = "androidx.annotation.keep", version.ref = "annotationKeep" }
    

    Gradle, eklentiyi Google'ın Maven deposundan indirir. Bu nedenle, pluginManagement dosyanızdaki settings.gradle.kts depoların google() içerdiğinden emin olun.

  2. Kök build.gradle.kts dosyanızda, eklentiyi uygulamadan AGP ile birlikte beyan edin:

    plugins {
        alias(libs.plugins.android.application) apply false
        // Other plugins.
    
        // The plugin relies on AGP classes that it doesn't bundle, so declare
        // it next to AGP. Declaring it here also gives every module the same
        // plugin version.
        alias(libs.plugins.androidx.annotation.keep) apply false
    }
    
  3. Yansıtma veya JNI kullanan her modülün build.gradle.kts dosyasında eklentiyi uygulayın ve kitaplık bağımlılığını ekleyin:

    plugins {
        alias(libs.plugins.android.application)
        // The plugin reads annotations only from this module's own code, so
        // apply it in every module that uses Jetpack Keep.
        alias(libs.plugins.androidx.annotation.keep)
    }
    
    dependencies {
        implementation(libs.androidx.annotation.keep)
    }
    
  4. Projenizi Gradle dosyalarıyla senkronize edin.

Eklenti, bir Android uygulama modülünün her derleme değişkeni için releaseExtractKeepRules gibi bir <variant>ExtractKeepRules görevi kaydeder ve oluşturulan kuralları bu değişken için R8'e iletir. Kitaplık modülleri için eklenti, oluşturulan kuralları kitaplığın çıkışında tüketici saklama kuralları olarak paketler. JVM kitaplık modülleri için ek kurulum gerekir.

Lint ile yansımayı bulma

Jetpack Keep, aşağıdaki lint kontrollerini içerir:

ReflectionWithoutKeepAnnotations
Eşleşen bir saklama ek açıklaması olmayan Class.forName, getDeclaredConstructor, getDeclaredMethod ve getDeclaredField gibi yansıtıcı çağrıları işaretler.
MissingKeepAnnotationPlugin
androidx.annotation.keep:keep'e bağlı olan ancak androidx.annotation.keep Gradle eklentisini uygulamayan modülleri işaretler. Eklenti olmadan derlemeniz, ek açıklamalarla saklama kuralları oluşturmaz.

Bir Android modülüne ek açıklama eklemek için lint'i kullanmak üzere aşağıdaki adımları uygulayın:

  1. Ek açıklama gerektiren yansıtıcı çağrıları bulmak için modülde lint çalıştırın:

    ./gradlew :app:lint
    
  2. Lint'in otomatik olarak yapabileceği düzeltmeleri uygulayın:

    ./gradlew :app:lintFix
    

    Lint, kodun hangi sınıfı ve üyeleri yansıttığını belirleyebildiğinde otomatik olarak ek açıklama ekleyebilir.

  3. Eklenen her ek açıklamayı inceleyerek kodunuzun yaptığı işlemle eşleştiğini doğrulayın. Örneğin, parametre türlerinin kodunuzun çağırdığı oluşturucu veya yöntemle eşleştiğinden emin olun.

  4. Android Studio'da kalan bulguları çözün. Lint, hedef sınıfı belirleyemediğinde hızlı düzeltme, tamamlamanız için TODO yer tutucusuyla birlikte ek açıklamayı ekler. Notu manuel olarak da ekleyebilirsiniz.

    Örneğin, aşağıdaki işlev sınıf adını parametre olarak alır. Bu nedenle lint, hangi sınıfı oluşturduğunu belirleyemez. Hızlı düzeltme, yer tutucu içeren bir ek açıklama ekler:

    @UsesReflectionToConstruct(
        className = TODO(),
        parameterTypes = []
    )
    fun createInstance(className: String): Any =
        Class.forName(className).getDeclaredConstructor().newInstance()
    

    Yer tutucuyu, kodunuzun oluşturduğu sınıfla değiştirin. Sınıf, derleme süresinde kullanılabiliyorsa className yerine classConstant kullanın. Kotlin'de, TODO() ifadesini değiştirene kadar kodunuz derlenmez.

  5. Mevcut kuralları kaldırmadan önce eklentinin ek açıklamalarınızdan hangi saklama kurallarını oluşturduğunu kontrol edin. Uygulama modülü için sürüm varyantına yönelik ayıklama görevini çalıştırın:

    ./gradlew :app:releaseExtractKeepRules
    

    Eklenti, kuralları app/build/generated/release/androidx.annotation.keep.rules.pro dosyasına yazar. Kitaplık modülleri için Oluşturulan kuralları doğrulama bölümüne bakın.

  6. Yeni ek açıklamaların yerini aldığı saklama kurallarını, saklama kuralları dosyalarınızdan kaldırın. Bir kural, başka bir modüldeki veya kitaplıktaki yansıtma gibi lint'in işaretlemediği yansıtmayı da kapsayabilir. Bu nedenle, kaldırmadan önce her kuralı kontrol edin:

    • -keep class com.example.** { *; } gibi paket genelinde bir kural için, ek açıklamaların veya diğer kuralların paketin sınıflarındaki tüm yansımaları kapsadığından emin olun.
    • Belirli sınıfları veya üyeleri saklayan bir kural için, bu kuralın oluşturulan kurallardan biriyle yakından eşleştiğinden ya da başka bir yansıtmanın bu kurala bağlı olmadığından emin olun.
  7. Yayınlanan derlemenizi test edin.

Kodunuza ek açıklama ekleme

Yansımayı gerçekleştiren koda bir Jetpack Keep ek açıklaması ekleyin: yansıtıcı çağrıyı içeren işlev, kurucu, alan veya sınıf. Ek açıklama, yansıtmanın hedefini açıklar ve oluşturulan kural, yalnızca R8 ek açıklamalı kodu tutarsa bu hedefi korur.

Kodunuzun işlevine göre bir ek açıklama seçin:

Ek Açıklama Kodunuzu şu durumlarda kullanabilirsiniz:
@UsesReflectionToConstruct Örneğin, getDeclaredConstructor().newInstance() ile yansıtma yoluyla bir oluşturucuyu çağırır.
@UsesReflectionToAccessMethod Yansıma yoluyla bir yöntem çağırır (ör. getMethod veya getDeclaredMethod ile).
@UsesReflectionToAccessField Yansıma yoluyla bir alanı okur veya yazar (ör. getField veya getDeclaredField ile).
@UnconditionallyKeep Uygulamanızın Kotlin veya Java kodunun dışından (örneğin, JNI aracılığıyla yerel koddan) çağrılır. Bu ek açıklamayı yalnızca koşullu kurallar oluşturan @UsesReflection* ek açıklamalarını kullanamadığınızda kullanın.

Tüm @UsesReflection* ek açıklamaları şu kurallara uyar:

  • Hedef sınıfı classConstant veya className ile tanımlayın. Sınıf derleme zamanında kullanılabiliyorsa classConstant kullanın. Derleyici, referansı kontrol eder ve yeniden adlandırma gibi IDE yeniden düzenlemeleri referansı günceller. className yalnızca sınıf derleme zamanında kullanılamadığında (ör. isteğe bağlı bağımlılıktaki bir sınıf) kullanılmalıdır. Bu iki parametre birbirini dışlar.
  • En spesifik sınıfı hedefleyin. Oluşturulan kurallar, belirttiğiniz sınıftaki ve bu sınıfın alt sınıflarındaki veya arayüz uygulamalarındaki eşleşen üyeleri korur. Sınıf ne kadar genel olursa R8 o kadar fazla kod tutar.
  • Tek bir aşırı yükleme tutmak için parametre türlerini belirtin. parameterTypes veya parameterTypeNames değerini ayarlamazsanız kurallar, tüm oluşturucuları ya da yöntemin tüm aşırı yüklemelerini korur. Yalnızca bağımsız değişken içermeyen oluşturucuyu veya yöntemi tutmak için boş bir dizi iletin.
  • Birden fazla hedef için notu tekrarlayın. Bir işlev birden fazla sınıfı veya üyeyi yansıtıyorsa her hedef için bir ek açıklama ekleyin.

Yansıtma ile nesneler oluşturma

Kodunuz yansıtma yoluyla bir sınıfı örneklediğinde @UsesReflectionToConstruct kullanın. Aşağıdaki örnekte, bir görev çalıştırıcı, StartupTask arayüzünün uygulamalarını ada göre örneklendirir. Ek açıklama, arayüzü belirttiğinden, oluşturulan kurallar, R8 execute'i sakladığı sürece her StartupTask uygulamasının bağımsız değişken içermeyen kurucusunu saklar:

interface StartupTask {
    fun run()
}

object TaskRunner {
    // The task class is known only at runtime, so target the interface that
    // every task implements.
    @UsesReflectionToConstruct(classConstant = StartupTask::class, parameterTypes = [])
    fun execute(className: String) {
        val task = Class.forName(className)
            .getDeclaredConstructor()
            .newInstance() as StartupTask
        task.run()
    }
}

Yansıtma ile yöntemleri çağırma

Kodunuz yansıma yoluyla bir yöntem çağırdığında @UsesReflectionToAccessMethod kullanın. methodName değerini yöntemin adı olarak ayarlayın. Aşağıdaki örnekte, kod isteğe bağlı bir bağımlılıkta statik bir yöntemi çağırır. Sınıf, derleme süresinde kullanılamadığından ek açıklama classConstant yerine className kullanır:

object CrashReporting {
    @UsesReflectionToAccessMethod(
        className = "com.example.crashlib.CrashReporter",
        methodName = "log",
        // Omitting parameterTypes keeps every log overload. Keep only the one
        // that the code calls.
        parameterTypes = [String::class],
    )
    fun log(message: String) {
        val reporterClass = try {
            Class.forName("com.example.crashlib.CrashReporter")
        } catch (e: ClassNotFoundException) {
            // Crash reporting is optional, so skip it instead of failing.
            return
        }
        reporterClass.getMethod("log", String::class.java).invoke(null, message)
    }
}

Yansıma ile alanlara erişme

Kodunuz yansıtma yoluyla bir alanı okuduğunda veya yazdığında @UsesReflectionToAccessField kullanın. fieldName alanını alanın adıyla değiştirin:

class UserProfileFetcher {
    // User doesn't expose internalId, so reading it requires reflection.
    @UsesReflectionToAccessField(classConstant = User::class, fieldName = "internalId")
    fun readInternalId(user: User): Any? {
        val field = User::class.java.getDeclaredField("internalId")
        field.isAccessible = true
        return field.get(user)
    }
}

Uygulamanızın dışından çağrılan kodu koruma

Uygulamanızın Kotlin veya Java kodunun dışından çağrılan kodlar için @UnconditionallyKeep kullanın (ör. yerel kodun JNI aracılığıyla çağırdığı bir yöntem). Çağrılan kodu değil, çağrılan öğeyi açıklama olarak ekleyin:

class NativeBridge {
    // Only native code calls this method, through a JNI lookup by name that
    // R8 can't see. Keep the method and its name unconditionally.
    @UnconditionallyKeep
    fun onNativeEvent(message: String) {
        Log.d("NativeBridge", "Received from native code: $message")
    }

    // The default proguard-android-optimize.txt file already keeps native
    // method names, so this method doesn't need an annotation.
    external fun startNativeProcess()
}

Oluşturulan kural, ek açıklamalı yöntemi ve bu yöntemi içeren sınıfı orijinal adlarıyla korur. Yöntemin imzasının referans verdiği diğer sınıfları korumaz. Yerel kod bu sınıflara bağlıysa bunlara da ek açıklama ekleyin veya bunlar için saklama kuralları ekleyin. JNI ve R8 hakkında daha fazla bilgi için JNI aracılığıyla yerel kodla etkileşim başlıklı makaleyi inceleyin.

Varsayılan olarak @UnconditionallyKeep, öğenin adını korur. Harici çağrı yapan, ada bağlı değilse R8'in öğeyi yeniden adlandırabilmesi için shouldPreserveName = false değerini ayarlayın.

Oluşturulan kuralları doğrulama

Bu bölümde, her modül türü için oluşturulan kuralların nasıl kontrol edileceği açıklanmaktadır. Bir uygulama modülü için eklenti, kuralları doğrudan R8'e iletir. Kitaplık modülü için eklenti, kuralları kitaplığın çıkışında tüketici saklama kuralları olarak paketler. Kitaplığınıza bağlı uygulamalar, Jetpack Keep'i kullanmasalar bile R8'i çalıştırdıklarında bu kuralları otomatik olarak uygular. Tüketici saklama kuralları hakkında daha fazla bilgi için Kitaplık yazarları için optimizasyon başlıklı makaleyi inceleyin.

Android uygulamaları

Eklentinin bir uygulama modülü için hangi saklama kurallarını oluşturduğunu kontrol etmek istiyorsanız bir derleme değişkeni için ayıklama görevini çalıştırın:

./gradlew :app:releaseExtractKeepRules

Eklenti, her varyantın kurallarını app/build/generated/<variant>/androidx.annotation.keep.rules.pro dosyasına yazar. Her kuraldan önce gelen # context: yorumu, kuralı oluşturan ek açıklamalı kodu tanımlar.

Oluşturulan kuralların diğer saklama kurallarınızla birlikte optimizasyonu nasıl etkilediğini kontrol etmek için R8 Yapılandırma Analiz Aracı'nı kullanın.

Android kitaplıkları

Android kitaplık modülleri için ek kurulum gerekmez. AAR'yi oluşturduğunuzda eklenti, oluşturulan kuralları AAR'nin proguard.txt dosyasına ekler. Bu işlem, AAR'nin halihazırda içerdiği tüketici saklama kurallarından sonra yapılır. Eklenti, bunu yapmak için her derleme değişkenine bir <variant>KeepRulesTransformAar görev kaydeder.

Kuralları doğrulamak için AAR'yi (ör. ./gradlew :mylibrary:assembleRelease ile) derleyin ve oluşturulan AAR'de proguard.txt dosyasını açın. Oluşturulan kurallar, aşağıdaki başlığa sahip bir bölümde gösterilir:

#################################################################
#   Rules generated from androidx.annotation.keep annotations   #
#################################################################

macOS ve Linux'ta kuralları komut satırından da yazdırabilirsiniz:

unzip -p mylibrary/build/intermediates/aar/release/releaseKeepRulesTransformAar/mylibrary-release.aar proguard.txt

Kotlin Multiplatform kitaplıkları

Android hedefi olan Kotlin Multiplatform kitaplık modülleri için de ek kurulum gerekmez. Android hedefi için AAR'yi oluşturduğunuzda eklenti, oluşturulan kuralları Android kitaplıklarında olduğu gibi AAR'nin proguard.txt dosyasına ekler. Eklenti, bunu yapmak için bir androidMainKeepRulesTransformAar görevi kaydeder.

Kuralları doğrulamak için bu görevi çalıştırın ve oluşturduğu AAR'de proguard.txt dosyasını açın. macOS ve Linux'ta kuralları komut satırından yazdırabilirsiniz:

./gradlew :mylibrary:androidMainKeepRulesTransformAar
unzip -p mylibrary/build/intermediates/aar/androidMain/androidMainKeepRulesTransformAar/mylibrary.aar proguard.txt

JVM kitaplıkları

Bir JVM kitaplığı modülünün kurallarını doğrulamadan önce, eklentinin oluşturulan kuralları JAR'ının bir kopyasına eklemesi için modülü yapılandırın. Ardından, kaydettiğiniz dönüştürme görevini çalıştırın ve oluşturduğu JAR'daki kurallar dosyalarını kontrol edin. Örneğin, görevi keepRulesJar olarak adlandırdıysanız macOS ve Linux'ta kuralları aşağıdaki komutlarla yazdırabilirsiniz:

./gradlew :mylibrary:keepRulesJar
unzip -p mylibrary/build/intermediates/annotation-keep/keepRulesJar/mylibrary-keep-rules.jar \
    "META-INF/com.android.tools/*" "META-INF/proguard/*"

JVM kitaplıklarını yapılandırma

JAR oluşturan JVM kitaplığı modüllerinde eklenti, JAR'ı otomatik olarak değiştirmez. Bunun yerine, oluşturulan kurallarla JAR'ın bir kopyasını oluşturan bir görevi kaydetmek ve ardından bu JAR'ı yayınlamak için annotationKeep.registerJavaArchiveTransform işlevini kullanın. Kitaplık modülünün build.gradle.kts dosyasına aşağıdakileri ekleyin:

plugins {
    id("java-library")
    alias(libs.plugins.jetbrains.kotlin.jvm)
    alias(libs.plugins.androidx.annotation.keep)
}

val jarTask = tasks.named<Jar>("jar")

// Apps run R8 on this library's code, so the generated rules must be inside
// the JAR. The plugin handles this automatically only for Android modules.
val keepRulesJarTask = annotationKeep.registerJavaArchiveTransform(
    taskName = "keepRulesJar",
    input = jarTask.flatMap { it.archiveFile },
    fileName = jarTask.flatMap { it.archiveFileName }.map { it.replace(".jar", "-keep-rules.jar") },
)

// Consumers of this library resolve apiElements and runtimeElements, so add
// the JAR that contains the generated rules to them.
configurations.configureEach {
    if (name == "apiElements" || name == "runtimeElements") {
        outgoing.artifact(keepRulesJarTask.flatMap { it.outputJar })
    }
}

dependencies {
    implementation(libs.androidx.annotation.keep)
}

Dönüşüm, oluşturulan kuralları JAR'ın hedeflenen R8 kuralları konumuna ekler, META-INF/com.android.tools/r8/proguard.txt. JAR, META-INF/com.android.tools/ veya META-INF/proguard/ içinde zaten kurallar içeriyorsa dönüştürme, oluşturulan kuralları mevcut kurallar dosyasına ekler. Bu konumlar hakkında daha fazla bilgi için Farklı R8 sürümlerini destekleme başlıklı makaleyi inceleyin.

Dönüşümü kaydettikten sonra JAR'daki kuralları doğrulayın.

Örnek: İsteğe bağlı bir eklenti kitaplığı yükleme

Kitaplıklar, isteğe bağlı eklentileri yüklemek için genellikle yansımayı kullanır. Örneğin, bir analiz SDK'sı temel kitaplık ve video eklentisi sunabilir. Eklenti, temel kitaplığa bağlı olduğundan temel kitaplık, eklentinin sınıflarına doğrudan referans veremez. Bunun yerine, çekirdek kitaplık çalışma zamanında eklentinin sınıfını ada göre arar ve yalnızca sınıfı bulursa video analizini etkinleştirir.

Çekirdek kitaplık, eklentinin sınıfını yalnızca yansıma yoluyla kullandığından R8, sınıfı veya kurucusunu uygulamanın yayınlanan derlemesinden kaldırabilir. Bu durumda arama başarısız olur. Hata yalnızca eklentiyi içeren uygulamaların yayınlanan derlemelerinde meydana geldiği için test sırasında yakalanması zordur.

Jetpack Keep olmadan, eklenti sınıfı için bir tüketici saklama kuralı gönderir. Bu durum, İsteğe bağlı bağımlılıkları desteklemek için yansıtma bölümünde gösterilmiştir:

# In the add-on's consumer keep rules file.
-keep class com.example.analytics.video.VideoEventTracker {
    <init>();
}

Bu kural, kuralı gerektiren aramadan farklı bir kitaplıkta olduğundan ikisi arasında senkronizasyon sorunu yaşanabilir. Jetpack Keep ile bunun yerine çekirdek kitaplıktaki aramaya ek açıklama eklersiniz:

// In the core library.
interface AnalyticsPlugin {
    fun start()
}

object AnalyticsManager {
    // The annotation and the lookup share this constant so that they can't
    // refer to different classes.
    private const val VIDEO_TRACKER_CLASS =
        "com.example.analytics.video.VideoEventTracker"

    @UsesReflectionToConstruct(
        className = VIDEO_TRACKER_CLASS,
        parameterTypes = [],
    )
    fun initialize() {
        val tracker = try {
            Class.forName(VIDEO_TRACKER_CLASS)
                .getDeclaredConstructor()
                .newInstance() as AnalyticsPlugin
        } catch (e: ClassNotFoundException) {
            // Apps that don't play video leave out the add-on, so a missing
            // class isn't an error.
            return
        }
        // This is an ordinary interface call, not a reflective one, so R8
        // keeps start() without an annotation.
        tracker.start()
    }
}

Ek açıklama aşağıdaki yönergelere uygundur:

  • Yansıtmayı gerçekleştiren koda ek açıklama ekleyin. Ek açıklama, eklentinin sınıfında değil, Class.forName yöntemini çağıran initialize yönteminde.
  • Kodun işlevine göre ek açıklama seçin. Arama, sınıfın bir örneğini oluşturur. Bu nedenle kod, @UsesReflectionToConstruct kullanır.
  • Referans veremediğiniz bir sınıf için className kullanın. Eklentinin sınıfı, temel kitaplığın derleme sınıf yolunda olmadığından ek açıklama classConstant kullanamaz.
  • Yalnızca kodun çağırdığı oluşturucuyu tutun. Arama, bağımsız değişken içermeyen oluşturucuyu çağırır. Bu nedenle parameterTypes boş bir dizidir.

Çekirdek kitaplığın AAR'sini oluşturduğunuzda eklenti, R8 initialize öğesini her tuttuğunda VideoEventTracker ve bağımsız değişken içermeyen kurucusunu tutan bir kural ekler. SDK'yı kullanan uygulamalar, Jetpack Keep'i kullanmasalar bile bu kuralı otomatik olarak uygular. Bu nedenle, eklentinin kendi kuralı olması gerekmez. Bu yaklaşım, eklentinin kuralına kıyasla aşağıdaki avantajları sunar:

  • Kural, arama işleminin yanında kalır. Çekirdek kitaplık hem aramaya hem de kurala sahip olduğundan, iki kitaplık arasında bir değişikliği koordine etmek yerine bunları birlikte güncellersiniz.
  • R8, kullanılmayan kodları kaldırmaya devam edebilir. Bir uygulama eklentiyi içeriyor ancak initialize yöntemini hiç çağırmıyorsa oluşturulan kural geçerli olmaz ve R8, VideoEventTracker yöntemini kaldırabilir. Eklentinin kuralı, eklentiyi içeren her uygulamada eklentiyi tutar.
  • Hata analizi, açıklama eklenmemiş aramaları yakalar. Temel kitaplığa daha sonra ek açıklama içermeyen başka bir yansıtıcı arama eklenirse lint bunu işaretler. Böylece, kitaplığı yayınlamadan önce düzeltebilirsiniz.

Sonucu onaylamak için oluşturulan kuralın, Android kitaplıkları bölümünde açıklandığı gibi AAR'nin proguard.txt dosyasında göründüğünden emin olun. Ardından, eklentiyi içeren bir uygulamanın yayınlanan derlemesini test edin. Eksik bir kural, video analizlerini bozabilir.

Ek kaynaklar