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

Jetpack Keep, yansıma ve Java Native Interface (JNI) kullanımını ayrı saklama kuralları dosyaları yerine doğrudan Kotlin veya Java kaynak kodunuzda ek açıklamalarla bildirmenize olanak tanır. Jetpack Keep Gradle eklentisi, derleme sırasında bu ek açıklamaları 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ı, bu kurallara ihtiyaç duyan 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ıtmalı çağrının hedefini yalnızca çağrıyı yapan kodu da tutuyorsa tutar. Çağrı kodu kullanılmıyorsa ve R8 bu kodu kaldırırsa (genellikle isteğe bağlı kitaplık özelliklerinde olduğu gibi) R8, hedefi de kaldırabilir. Böylece, kullanılmayan özelliklerden gelen kurallar gereksiz yer kaplamaz.

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

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 kurucusunu kaldırır ve uygulama, çalışma zamanında kilitlenir. 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, yapıcı için bir saklama kuralı oluşturur. Önceki manuel olarak yazılmış kuralın aksine, oluşturulan kural koşulludur: R8, yalnızca loadResourceLocator optimize edilmiş uygulamada kalırsa yapıcıyı korur.

Ş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üşümü 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, settings.gradle.kts dosyanızdaki pluginManagement depolarını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ıma 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 <variant>ExtractKeepRules gibi bir releaseExtractKeepRules görevi kaydeder ve oluşturulan kuralları ilgili 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çıklamalardan saklama kuralları oluşturmaz.

Bir Android modülüne ek açıklama eklemek için lint'i kullanmak istiyorsanız 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ığı kurucu 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, ek açıklamayı tamamlamanız için TODO yer tutucuyla 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, lint'in işaretlemediği yansımaları da (ör. başka bir modüldeki veya kitaplıktaki yansıma) kapsayabilir. Bu nedenle, kaldırmadan önce her kuralı kontrol edin:

    • -keep class com.example.** { *; } gibi paket genelinde geçerli 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ın, oluşturulan kurallardan biriyle yakından eşleştiğinden veya başka bir yansımanı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 saklarsa bu hedefi saklar.

Kodunuzun ne yaptığına göre bir ek açıklama seçin:

Ek Açıklama Kodunuzun kullanıldığı durumlar
@UsesReflectionToConstruct Yansıma yoluyla bir oluşturucuyu çağırır (ör. getDeclaredConstructor().newInstance() ile).
@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 (ör. 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.

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

  • Hedef sınıfı classConstant veya className ile tanımlayın. Sınıf, derleme süresinde kullanılabiliyorsa classConstant kullanın. Derleyici, referansı kontrol eder ve yeniden adlandırma gibi IDE yeniden düzenlemeleri referansı günceller. className ifadesini yalnızca sınıf, derleme süresinde kullanılamadığında (ör. isteğe bağlı bir bağımlılıktaki sınıf) kullanın. 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 sağlamak için parametre türlerini belirtin. parameterTypes veya parameterTypeNames değerini ayarlamazsanız kurallar, yöntemin tüm kurucularını veya 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ıma ile nesne oluşturma

Kodunuz, yansıma yoluyla bir sınıfı örneklendirdiğ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ği için oluşturulan kurallar, R8 execute'i koruduğu sürece her StartupTask uygulamasının bağımsız değişken içermeyen kurucusunu korur:

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ıma 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öntem ç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ıma yoluyla bir alanı okuduğunda veya yazdığında @UsesReflectionToAccessField kullanın. fieldName değerini alanın adı olarak ayarlayın:

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 öğeye ek açıklama 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. 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, AAR'nin halihazırda içerdiği tüm tüketici saklama kurallarından sonra ekler. 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ığın yer aldığı 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ık 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 kural dosyalarını kontrol edin. Örneğin, göreve keepRulesJar adını verdiyseniz 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 üreten JVM kitaplık modülleri için eklenti, JAR'ı otomatik olarak değiştirmez. Bunun yerine, oluşturulan kurallarla JAR'ın bir kopyasını oluşturan bir görev kaydetmek için annotationKeep.registerJavaArchiveTransform işlevini kullanın ve ardından bu JAR'ı yayınlayı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 kurallar içeriyorsa dönüşüm, oluşturulan kuralları mevcut kural dosyasına ekler. Bu konumlar hakkında daha fazla bilgi için Farklı R8 sürümlerini destekleme bölümüne bakın.

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ıma 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 oluştuğundan test sırasında fark edilmesi 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ıma 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()
    }
}

Not, aşağıdaki kurallara uymalıdır:

  • 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 öğesini kullanamaz.
  • Yalnızca kodun çağırdığı oluşturucuyu saklayın. 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ına ihtiyacı yoktur. Eklentinin kuralıyla karşılaştırıldığında bu yaklaşımın aşağıdaki avantajları vardır:

  • Kural, aramanın 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.
  • Lint, ek açıklama eklenmemiş aramaları yakalar. Çekirdek kitaplık daha sonra ek açıklama içermeyen başka bir yansıtmalı arama eklerse 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.

Sınırlamalar

Jetpack Keep, sınıfları ve üyeleri ek açıklamalarına göre bulan yansımayı henüz algılamaz veya bunun için saklama kuralları oluşturmaz. Bu özellik planlanmıştır.

Bu tarihe kadar, bu tür bir yansıtma için saklama kurallarını kullanın. Örneğin, aşağıdaki kural, R8'in sakladığı sınıflarda @SomeAnnotation ile ek açıklama eklenen her üyeyi saklar:

-keepclassmembers class * {
    @com.example.annotation.SomeAnnotation <methods>;
    @com.example.annotation.SomeAnnotation <fields>;
}

Jetpack Keep kullanım alanınıza uygun değilse veya başka geri bildirimleriniz varsa sorun bildirin.

Ek kaynaklar