R8 ile platform kodunu ve kaynaklarını optimize etme

Android platformu derleme sistemi (Soong), Android uygulamaları (android_app), testler (android_test) ve compile_dex: true ile yüklenebilir Java kitaplıkları (ör. services.jar) dahil olmak üzere DEX bayt kodu derleyen hedeflerde R8 derleyicisini çalıştırarak kullanılmayan kod ve kaynakları küçültür, optimize eder ve kaldırır. Soong, statik kitaplıklarda (android_library ve static java_library) R8'i doğrudan çalıştırmaz ancak optimize özellik bloğunu kullanarak tüketici koruma kurallarını (export_proguard_flags_files: true) statik olarak bağlayan aşağı akış DEX hedeflerine ekler ve yayar.

Sistem görüntüleri veya satıcı paketleri oluşturan platform mühendisleri için aşırı geniş kapsamlı koruma kurallarının ortadan kaldırılması, doğrudan sistem sağlığı avantajları sunar:

  • Daha küçük sistem bölümü alanı: R8, APK'ları ve JAR'ları /system, /system_ext, /product ve /vendor üzerine paketlemeden önce kullanılmayan sınıfları, yöntemleri ve kaynakları kaldırır.
  • Daha küçük derlenmiş yapılar: Daha az DEX yöntemi, derleme sırasında veya cihaz üzerinde derleme sırasında dex2oat tarafından oluşturulan daha küçük .odex ve .vdex dosyaları anlamına gelir.
  • Daha düşük çalışma zamanı bellek baskısı: Android, .odex, .vdex ve APK kaynak tablolarını talep üzerine sayfalama mmap çağrılarını kullanarak işlem belleğine eşlediğinden, daha küçük ikili dosyalar uygulama başlatma sırasında büyük sayfa hatalarını azaltır ve işlemler genelinde yerleşik kod ile kaynak belleğini düşürür. Derlenmiş uygulama kodunun cihaz belleğini nasıl etkilediği hakkında daha fazla bilgi için Uygulama kodu bellektir başlıklı makaleyi inceleyin.
  • Daha etkili program genelinde optimizasyon: Dar tutma kısıtlamaları, R8'in satır içi yöntemleri kullanmasına, arayüzü ve sanal çağrıları sanallaştırmasına, kullanılmayan alanları temizlemesine ve sabitleri sınıflar arasında yaymasına olanak tanır.

Soong'da R8 optimizasyonunu yapılandırma

Android.bp dosyalarında, android_app ve yüklenebilir java_library hedeflerinde (veya tüketici saklama kurallarını dışa aktarmak için android_library ve statik java_library modüllerinde) optimize özelliğini kullanarak R8 optimizasyonunu yapılandırın:

android_app {
    name: "MySystemApp",
    srcs: ["src/**/*.java"],
    optimize: {
        obfuscate: true,
        shrink_resources: true,
    },
}

Aşağıdaki tabloda, Soong'daki en yaygın optimize özellikleri özetlenmektedir (build/soong/java/dex.go içinde tanımlanır):

Özellik Açıklama
enabled R8'in hedefte çalışıp çalışmayacağını kontrol eder. Tüm android_app hedefleri için varsayılan değer true olur.
shrink Ulaşılamayan sınıfları, alanları ve yöntemleri kaldırmak için ağaç budamayı kontrol eder. android_app hedefleri için varsayılan olarak true değerine ayarlanır (açıkça ayarlanmadığı sürece -dontshrink değerini ileten bağımsız java_library ve test modülleri için false).
optimize Yöntem satır içi yapma, sınıf birleştirme, sabit yayma ve kullanılmayan dal kaldırma gibi bayt kodu optimizasyonlarını kontrol eder. Varsayılan olarak, android_app hedefleri için true kullanılır (RELEASE_R8_OPTIMIZE_BY_DEFAULT yayınlanan derleme işaretiyle kontrol edilir).
obfuscate Tanımlayıcı küçültme ve yeniden adlandırma işlemlerini kontrol eder. Geçmişle uyumluluk için varsayılan olarak false kullanılır ancak DEX boyutunu küçültmek ve daha ayrıntılı optimizasyonlardan yararlanmak için mümkün olan her yerde true olarak ayarlayın (bkz. Mümkün olan her yerde karartmayı etkinleştirin).
shrink_resources Kod daraltma işleminden sonra paketlenmiş APK'dan kullanılmayan kaynakları (res/ girişleri) kaldırır. Varsayılan olarak false değerine ayarlanır.
optimized_shrink_resources Entegre R8 kodu ve kaynak küçültme ardışık düzenini çalıştırır. Böylece R8, tek bir geçişte kod ve kaynak referanslarını birlikte izler. shrink_resources: true ayarlandığında varsayılan olarak RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (standart platform derlemelerinde true) olur.
proguard_flags_files Özel saklama kuralları içeren modüle özgü .flags veya .pro dosyalarını listeler. Standart ek açıklamalar veya varsayılanlar yeterli olduğunda özel dosyalar eklemekten kaçının.
export_proguard_flags_files Bu kitaplığın proguard_flags_files değerini, statik olarak bağlı olduğu alt akış modüllerine yayar.
trace_references_from Bu hedefteki R8'in otomatik olarak izlediği ve koruduğu, bu hedefe referans veren Java kitaplığı eşlikçi hedeflerini listeler.

Mümkün olan her yerde kod karartmayı etkinleştirin

Soong'da, geçmişle uyumluluk için obfuscate varsayılan olarak false değerini alır ancak mümkün olan her yerde android_app ve bağımsız DEX hedeflerinde obfuscate: true değerini açıkça ayarlamanız gerekir:

  • Daha küçük DEX ve .vdex ayak izi: Paketleri, sınıfları, alanları ve yöntemleri kısa tanımlayıcılarla (a, b) yeniden adlandırmak DEX dize havuzunu (string_ids ve string_data_item) ve tür tanımlayıcılarını küçültür. Android, .vdex ve .odex dosyalarını işlem belleğine eşlediğinden daha küçük sembol tabloları hem sistem bölümü boyutunu hem de çalışma zamanı bellekte kaplanan yeri doğrudan azaltır.
  • Daha kapsamlı program optimizasyonları: R8'in tanımlayıcıları yeniden adlandırmasına izin vermek, R8'in ad çakışmalarını önlemek için atlaması gereken paketlerde sınıf birleştirme, paket düzleştirme ve sentetik sınıf tekilleştirme işlemlerini mümkün kılar.
  • Tam yığın izleme simgeselleştirme: Soong'da kod karartmayı etkinleştirmek hata ayıklanabilirliği azaltmaz. Soong, R8 ile derlenen her hedef için modülün ara dizininde bir proguard_dictionary eşleme dosyası oluşturur, tüm modül sözlüklerini derlemenin proguard-dict.zip yapıtında paketler, DEX başlığına bir eşleme karması (--map-id-template) yerleştirir ve sınıf SourceFile özelliklerini (--source-file-template) yeniden yazar. Böylece retrace, yığın izlerini otomatik olarak sembolleştirebilir.

obfuscate: false öğesini yalnızca şu amaçlarla saklayın (veya genel API adlarını açıkça koruyun):

  • Önyükleme sınıf yolu veya system_server sınıf yolu üzerindeki paylaşılan kitaplıklar: Harici bir API'yi kullanıma sunan modüller (java_library veya java_sdk_library), çalışma zamanında diğer modüller tarafından dinamik olarak bağlanır (ör. framework.jar, services.jar veya <uses-library> hedefleri). Bu modüller, obfuscate: false değerini korumalı ya da @KeepForApi veya -keep kurallarını (önyükleme sınıf yolu hedefleri için protect_api_surface: true ile birlikte) kullanarak API yüzeylerini açıkça korumalıdır. Böylece, saplamalarına karşı derlenen arayanlar, çalışma zamanında sınıf ve üye adlarını çözebilir.

Bir uygulamada obfuscate: true özelliğini etkinleştirmeden önce aşağıdaki taşıma ön koşullarını doğrulayın:

  • Harici test APK'sı bağımlılıkları: Harici bir android_test APK grubu instrumentation_for: "MyApp", MyApp'nin dahili sınıflarını veya yöntemlerini doğrudan çağırıyorsa bu sembollerin yeniden adlandırılması test çalışma zamanında NoSuchMethodError veya NoClassDefFoundError'ye neden olur. Testi, MyApp.impl statik olarak bağlanan kendi kendini enstrümanlaştıran bir APK olarak yapılandırmayı tercih edin, test kancalarını @VisibleForTesting ile açıklama ekleyin veya trace_references_from yapılandırın (bkz. 2. adım: Test odaklı koruma kurallarını taşıma). Böylece, testler için gereken dahili semboller korunurken uygulamanın geri kalanı karartılır.
  • Dize tabanlı yansıtma ve JNI: Bir modül, sınıf, yöntem veya alanları değişmez dize adlarıyla (Class.forName, getDeclaredMethod veya JNI FindClass ve GetMethodID) keep kuralları ya da ek açıklamalar olmadan ararsa karartma, bu hedefleri yeniden adlandırır ve çalışma zamanı aramalarını bozar. (Not: Açıklama eklenmemiş yansıtma da shrink: true ve optimize: true kapsamında başarısız olur.) Bu giriş noktalarına Keep Açıklamaları Kılavuzu (@UsesReflection, @UsedByReflection veya @UsedByNative) ile açıklama ekleyin. Böylece R8, adlarını korur ve modülün geri kalanını karartır.

Sıfır özel işaret ilkesine uyun

İdeal Android platform modülünde özel proguard.flags veya keep.xml dosyası yoktur. Android platform derlemesinde, özel saklama kurallarının büyük çoğunluğu gereksizdir:

  • Platform temelleri ve AAPT2: Giriş noktalarının çoğu, @Keep, JNI native yöntemleri ve @VisibleForTesting gibi küresel platform temelleri tarafından otomatik olarak korunur veya AndroidManifest.xml ve düzen kaynaklarından AAPT2 tarafından oluşturulur (bkz. Varsayılan platform koruma kurallarını anlama).
  • Hedeflenen alternatifler: Giriş noktalarının korunması gerektiği durumlarda, ayrılmış .flags dosyaları yerine bildirim sitesi ek açıklamaları (keepanno, @VisibleForTesting), dışa aktarılan kitaplık kuralları (export_proguard_flags_files: true) veya statik test bağlantısı tercih edin (bkz. Mevcut saklama kurallarını denetleme ve taşıma).

Özel bir proguard.flags veya keep.xml dosyası eklemeden ya da saklamadan önce kuralın varsayılan temel çizgiler tarafından zaten işlenip işlenmediğini veya kod açıklamalarına taşınıp taşınamayacağını doğrulayın.

Varsayılan platform saklama kurallarını anlama

Soong, build/soong/java/dex.go içinde yapılandırılan DEX derleme hedeflerindeki (android_app, android_test ve yüklenebilir java_library modülleri) her R8 çağırma işlemine aşağıdaki genel temel kuralları otomatik olarak iletir:

  • build/make/core/proguard.flags: @com.android.internal.annotations.VisibleForTesting ile açıklama eklenmiş sınıfları ve üyeleri tüm paketlerde, @VisibleForTesting (androidx.annotation.VisibleForTesting veya com.google.common.annotations.VisibleForTesting) ile açıklama eklenmiş sınıfları ve üyeleri ise android.**, com.android.** ve com.google.android.** paketlerinde korur. Ayrıca @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback ve @dalvik.annotation.optimization.** öğelerini korur.
  • build/make/core/proguard_basic_keeps.flags: Yığın izlemeleri, çalışma zamanı görünürlüğü ek açıklamaları (RuntimeVisible*Annotations), Exceptions ve AnnotationDefault özellikleri, native yöntemleri, Serializable üyeleri, @JavascriptInterface yöntemleri, Throwable(String) oluşturucuları, Parcelable$CREATOR alanları ve protobuf MessageLite alanları için SourceFile özelliklerini korur.
  • build/make/core/proguard/kotlin.flags: Belirli Kotlin meta ek açıklamaları (kotlin.Metadata ve kotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) için zararsız uyarıları devre dışı bırakır ve yayın derlemelerindeki Kotlin DebugMetadata ek açıklamalarını kaldırır.
  • build/make/core/proguard/checknotnull.flags: Yaygın null kontrolü yardımcı programı çağrılarını (com.google.common.base.Preconditions.checkNotNull ve dagger.internal.Preconditions.checkNotNull*) kısa bayt kodu null kontrolleriyle değiştirir. Bu dosya, çerçeve API sınırları genelinde açık istisna mesajlarını korumak için Objects.requireNonNull karakterini kasıtlı olarak atlıyor.
  • build/make/core/proguard/enumvalues.flags: Derleme yapılandırması tarafından devre dışı bırakılmadığı sürece enum türlerinde values ve valueOf yöntemlerini korur.
  • AAPT2 tarafından otomatik olarak oluşturulan kurallar: AAPT2, her kayıtlı Activity, Service, BroadcastReceiver, ContentProvider, BackupAgent, Application, özel View alt sınıfı, Preference alt sınıfı ve XML android:onClick yöntemi için tam koruma kuralları oluşturmak üzere birleştirilmiş AndroidManifest.xml, düzen XML'si ve tercih XML'si dosyalarını inceler.

Mevcut saklama kurallarını denetleme ve taşıma

Bir platform deposundaki mevcut proguard.flags veya keep.xml dosyalarını denetlerken her kuralı aşağıdaki dört adımlı hiyerarşiye göre sırayla değerlendirin:

1. adım: Gereksiz veya eski kuralları silin

Genel temel çizgiler, AAPT2 manifest ve düzen kuralları veya R8 varsayılanları tarafından zaten kapsanan kuralların yanı sıra artık mevcut olmayan sınıflara veya paketlere referans veren kuralları silin.

Bir .flags dosyasındaki tüm kurallar gereksizse dosyayı silin ve Android.bp'den proguard_flags_files öğesini kaldırın. Kalan optimize bloğu yalnızca varsayılan ayarları yeniden belirtiyorsa gereksiz bloğu kaldırın ve derleme dosyasını bpfmt -w Android.bp ile biçimlendirin. Bir paketi temizlerken alt dizinlerdeki yardımcı modüllerin (ör. Kotlin hedef varyantları) aynı işaretler dosyasına referans verip vermediğini kontrol edin ve bunları birlikte güncelleyin.

2. adım: Test odaklı saklama kurallarını taşıyın

Aşağıdaki kalıplardan birini kullanarak test odaklı saklama kurallarını özel üretim .flags dosyalarından taşıyın:

  • Uygulama kitaplığını testlere bağlama (static_libs): Bir android_test, bir uygulamanın paket içi veya dahili uygulama ayrıntılarını kullandığında, önerilen platform mimarisi uygulama kaynak dosyalarını bir android_library (MyApp.impl) içine yerleştirmek ve MyApp.impl öğesini hem MyApp hem de MyAppTests ile statik olarak bağlamaktır:

    android_library {
        name: "MyApp.impl",
        srcs: ["src/**/*.java"],
        manifest: "AndroidManifest.xml",
    }
    
    android_app {
        name: "MyApp",
        static_libs: ["MyApp.impl"],
        optimize: {
            obfuscate: true,
            shrink_resources: true,
        },
    }
    
    android_test {
        name: "MyAppTests",
        srcs: ["tests/**/*.java"],
        static_libs: ["MyApp.impl"],
    }
    

    Bu 3 modüllü yapı, MyAppTests'nın dahili sınıflara tam erişimle kendi kendine enstrümantasyon uygulayan bir test olarak çalışmasına, MyApp'nin testleri bozmadan obfuscate: true'ı serbestçe etkinleştirmesine, üretim APK'sında yalnızca test için giriş noktalarının gönderilmesini önlemeye ve test kodu değiştiğinde MyApp'nin yeniden oluşturulmasını ortadan kaldırmaya olanak tanır.

  • Test kancalarına @VisibleForTesting ekleyin: Harici bir test APK'sı, android_app hedefinde az sayıda dahili yöntem veya oluşturucu çağırıyorsa ve .impl kitaplığına yeniden yapılandırma pratik değilse bu bildirimlere @VisibleForTesting ekleyin. Küresel build/make/core/proguard.flags temelinde, özel .flags dosyaları olmadan android.**, com.android.** ve com.google.android.** paketlerindeki @VisibleForTesting öğeleri otomatik olarak korunur. (Bu ad alanlarının dışındaki tedarikçi modülleri için @UsedByReflection veya dışa aktarılan kitaplık kurallarını kullanın.)

  • Platform kitaplığı sınırları arasında trace_references_from kullanma: Testler, hedef uygulamayı statik olarak bağlayamadığında (örneğin, sistem sunucusu hizmetlerini veya framework-connectivity gibi platform JAR'larını kullanan testler), hedef modülde trace_references_from'yi test kaynaklarını içeren bir Java kitaplığı yardımcı modülüne işaret edecek şekilde yapılandırın. R8, yardımcı bayt kodu tarafından referans verilen tüm sınıfları ve üyeleri izler ve saklar.

3. adım: Kuralları sahip kitaplıktan dışa aktarın

Paylaşılan bir java_library veya android_library kendi dahili yansıtması ya da JNI geri çağırmaları için saklama kuralları gerektiriyorsa kuralları kitaplık hedefinde tanımlayın ve export_proguard_flags_files: true değerini ayarlayın:

java_library {
    name: "my-shared-library",
    srcs: ["src/**/*.java"],
    optimize: {
        proguard_flags_files: ["proguard.flags"],
        export_proguard_flags_files: true,
    },
}

Statik olarak bağlanan tüm aşağı akış android_app ve kitaplık hedefleri my-shared-library bu kuralları otomatik olarak devralır. Bu nedenle, aşağı akış uygulamalarının bunları kopyalaması gerekmez. Farklı dizinlerdeki birden fazla modülün tek bir kod kitaplığına bağlı olmayan bir kural kümesini paylaşması gerektiğinde, .flags dosyasını Android.bp içinde açık bir filegroup aracılığıyla yayınlayın. Böylece modüller, paket sınırları arasında :my-shared-flags öğesine sorunsuz bir şekilde referans verebilir. Kitaplık tüketicilerinin, aşağı akış uygulama optimizasyonunu kısıtlamadan kitaplık tüketicisi saklama kuralları oluşturmasıyla ilgili genel bilgi için Kitaplık yazarları için optimizasyon başlıklı makaleyi inceleyin.

4. adım: Kaynak kodundaki bildirimlere açıklama ekleyin

Yansıtma veya JNI aracılığıyla erişilen ve AAPT2 ya da genel temel çizgiler tarafından kapsanmayan bir sınıf, yöntem, oluşturucu veya alan olduğunda -keep dosyalarındaki ayrılmış .flags kurallarını Keep Annotations Kılavuzu'ndaki kaynak ek açıklamalarıyla değiştirin (keepanno Javadoc referansına bakın):

  • @UsesReflection: Yansıtmayı gerçekleştiren kodu kontrol ettiğinizde bu ek açıklamayı tercih edin. Hangi hedef sınıflara, yöntemlere veya alanlara dinamik olarak erişildiğini bildirmek için yansıtma çağrısı sitesine yerleştirin. @UsesReflection, açıklama eklenen çağrı sitesinin kendisinin erişilebilir olduğu ön koşulunu otomatik olarak kodladığından, çağrı sitesi kullanılmıyorsa R8 hem arayanı hem de yansıtıcı hedefi temizler.
  • @UsedByReflection: Class.forName ile yüklenen sınıflar gibi harici kod veya kitaplıklar tarafından yansıtıcı olarak oluşturulan veya çağrılan sınıflara, yöntemlere, alanlara ya da oluşturuculara yerleştirin. Bundle veya Settings anahtarlarından ya da dinamik sınıf yükleyiciler arasında yüklenen eklenti sınıflarından. R8'in yalnızca tam yansıtıcı sözleşmeyi koruması ve sınıfın kullanılmayan üyelerini optimize etmeye veya budamaya devam edebilmesi için kind (ör. KeepItemKind.CLASS_AND_METHODS), preconditions (@KeepCondition) ve parametre kısıtlamalarını belirtin. AAPT2 bu bileşenleri zaten sakladığı için JobService veya BroadcastReceiver gibi manifest'e kaydedilmiş bileşenlere @UsedByReflection eklemeyin.
  • @UsedByNative: GetMethodID, GetStaticMethodID veya GetFieldID kullanılarak C veya C++ JNI kodundan erişilen yöntemlere ya da alanlara yerleştirin.
  • @KeepForApi: Kitaplığın kendisi dağıtımdan önce küçültüldüğünde bozulmadan kalması gereken kitaplık API sınıflarına veya üyelere yerleştirin.
  • @Keep (androidx.annotation.Keep): keepanno geçerli olmadığında yedek olarak kullanın. Bir sınıfa @Keep uygulandığında sınıf ve tüm üyeleri koşulsuz olarak korunur. Bir yönteme veya alana @Keep uygulanması, sınıf hiçbir zaman örneklenmemiş olsa bile üyenin ve onu içeren sınıfın korunmasını sağlayan koşulsuz bir giriş noktası görevi görürken keepanno koşullu erişilebilirliği ifade eder.

Soong modülünde R8 keepanno ek açıklamalarını kullanmak için:

  1. Android.bp uygulamasında libs bölümüne "keepanno-annotations" adlı etkinliği ekleyin:

    libs: [
        "keepanno-annotations",
    ],
    
  2. com.android.tools.r8.keepanno.annotations.* öğesini içe aktarın ve Java veya Kotlin kaynak kodundaki bildirimi ya da çağrı sitesini açıklama olarak ekleyin:

    import com.android.tools.r8.keepanno.annotations.KeepItemKind;
    import com.android.tools.r8.keepanno.annotations.UsedByReflection;
    
    public final class CustomPluginController {
        @UsedByReflection(
            description = "Instantiated via Class.forName from plugin config",
            kind = KeepItemKind.CLASS_AND_METHODS)
        public CustomPluginController(Context context) {
            // ...
        }
    }
    

    R8, keepanno ek açıklamalarını doğrudan kendi dahili koruma kuralı modeline çevirir ve ek açıklamaları nihai DEX çıkışından kaldırarak çalışma zamanı bayt kodu ek yükünü sıfıra indirir.

Yaygın hatalardan kaçının

Aşağıdaki bölümlerde, platform kodundaki sık karşılaşılan proguard.flags ve keep.xml tuzakları ve bunların nasıl düzeltileceği açıklanmaktadır.

Geniş bileşen saklama kuralları

# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
  • Performansı neden olumsuz etkiler? Bu kural, AAPT2 ile tamamen gereksizdir. Bileşenin son birleştirilmiş AndroidManifest.xml içinde kayıtlı olup olmadığını kontrol etmeden joker karakter kullandığı için R8, kullanılmayan kitaplık bileşenleri ve devre dışı bırakılmış hata ayıklama etkinlikleri de dahil olmak üzere sınıf yolunda bulunan her Activity, Service veya BroadcastReceiver alt sınıfını korumaya zorlar.
  • Önerilen düzeltme: Kuralı silin. AAPT2, birleştirilmiş manifesti inceler ve kayıtlı bileşenler için tam -keep kuralları oluşturur.

Geniş XML tıklama işleyici saklama kuralları

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • Performansı neden olumsuz etkiler? Modüldeki her sınıfta her public void *(View) yönteminin korunması, R8'in tek bir View parametresini kabul eden yöntemleri kaldırmasını veya satır içi yapmasını engeller.
  • Önerilen düzeltme: Kuralı silin. AAPT2, düzen XML dosyalarını tarar ve android:onClick özellikleri tarafından referans verilen yöntemler için hedeflenen saklama kuralları oluşturur.

Geniş Görünüm alıcı ve belirleyici koruma kuralları

# Don't do this:
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
    public *** get*();
}
  • Performansı neden olumsuz etkiler? Bu kural, R8'i uygulamadaki her View alt sınıfında ve tüm bağlı kitaplıklarda (AndroidX ve Materyal kitaplıkları dahil) her alıcı ve belirleyiciyi tutmaya zorlar. Bu durum, ölü yöntemlerin kaldırılmasını ve kullanıcı arayüzü kodunda satır içi işlemleri engeller.
  • Önerilen düzeltme: Kuralı silin. AAPT2, düzen XML dosyalarından şişirilmiş özel View sınıfları için oluşturucuları zaten korur. Kod, ObjectAnimator.ofFloat(view, "translationZ", ...) gibi yansıtıcı dize adlarını kullanarak bir görünüm özelliğine animasyon uyguluyorsa yansıtmayı tamamen önlemek için dize adını yazılmış bir özellik referansıyla (View.TRANSLATION_Z veya özel bir FloatProperty ya da IntProperty uygulaması) değiştirin. Yansıtıcı özellik erişiminden kaçınılamıyorsa belirli alıcı veya belirleyiciyi @UsedByReflection ile ek açıklama ekleyin.

Genel optimizasyon veya karartma devre dışı bırakılır.

# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
  • Performansa neden zarar veriyor? -dontoptimize veya -dontshrink öğelerini .flags dosyasına yerleştirmek, modülün Android.bp ayarlarını sessizce geçersiz kılar ve hedef genelinde optimizasyon geçişlerini devre dışı bırakır. Daha da kötüsü, bir kitaplık -dontoptimize içeren bir .flags dosyası dışa aktarırsa kitaplığı bağlayan her aşağı akış android_app için R8 optimizasyonunu devre dışı bırakır.
  • Önerilen düzeltme: .flags dosyalarından -dontoptimize, -dontshrink ve -dontobfuscate öğelerini kaldırın. Android.bp kullanarak optimizasyon davranışını açıkça kontrol edin. Bunun için yaprak hedefteki optimize bloğunu (shrink, optimize, obfuscate) kullanın.

Onaylanmış kurallardaki teşhis işaretleri

# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
  • Neden derlemeye zarar veriyor?: Teşhis işaretleri, derleme günlüklerini doldurur veya korumalı alan Soong derlemeleri sırasında çıkış dosyalarını yerel yollara yazmaya çalışır.
  • Önerilen düzeltme: Bu işaretleri, işlenmiş .flags dosyalarından silin. Soong, proguard_dictionary ve proguard_usage.zip gibi R8 eşleme ve kullanım çıkışlarını otomatik olarak out/soong/.intermediates/ altındaki modülün ara dizinine yazar.

Test kodu için üretimde saklama kuralları

  • Performansı neden olumsuz etkiler? Yalnızca test erişimi için özel -keep kuralları eklemek, bu kodun karartılmadan kalmasına ve üretim derlemelerinde tutulmasına neden olur. Yöntemleri @VisibleForTesting ile açıklama ekleme veya trace_references_from yapılandırma, manuel -keep kurallarının korunmasını önlese de bu semboller üretim ikilisine gönderilmeye devam eder.
  • Önerilen düzeltme: Yalnızca test için kullanılan giriş noktalarını üretim APK'sından tamamen çıkarmak amacıyla uygulamayı bir .impl kitaplığı olarak yapılandırın ve static_libs kullanarak kendi kendini izleyen bir test APK'sına bağlayın. Bu işlem, 2. adım: Test odaklı koruma kurallarını taşıma bölümünde açıklanmıştır.

keep.xml dosyasında geniş kapsamlı kaynak joker karakterleri

<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@raw/*,@drawable/*,@string/*" />
  • Performansı neden olumsuz etkiler?: Genel joker karakterler, modül ve bağımlılıklarındaki eşleşen türdeki her kaynağı korur. Bu durum, kaynak küçültmeyi (shrink_resources: true) engeller ve APK ile eşlenmiş resources.arsc tablosunu şişirir.
  • Önerilen düzeltme:
    • keep.xml yerine statik kaynak referanslarını tercih edin: Numaralandırılmış deneme dizeleri veya temalı çizilebilir öğeler gibi sınırlı bir kaynak grubunu dinamik olarak aramak için Resources.getIdentifier yöntemini kullanmaktan kaçının. Bunun yerine, derleme zamanı switch ifadesi kullanın veya statik R.id, R.string ya da R.drawable sabitlerini eşleyin. Statik referanslar, R8 ve AAPT2'nin tam olarak canlı kaynakları izlemesine, çalışma zamanı dize arama ek yükünü ortadan kaldırmasına ve keep.xml'nin tamamen kaldırılmasına olanak tanır.
    • Belirli kaynak adlarını listeleyin: Üçüncü taraf lisans okuyucusu gibi dinamik arama gerekiyorsa tools:keep içinde tam kaynak tanımlayıcılarını listeleyin (örneğin, tools:keep="@raw/third_party_licenses").
    • Gereksiz keep.xml dosyalarını silin: Listelenen kaynaklara kodda (R.raw.foo) veya XML'de (@raw/foo) statik olarak referans veriliyorsa küçültücü bunları otomatik olarak korur. Bu nedenle res/raw/keep.xml öğesini silebilirsiniz.

Kural değişikliklerini doğrulama ve denetleme

proguard.flags veya keep.xml'de saklama kurallarını her kaldırdığınızda ya da daralttığınızda, modülün sorunsuz bir şekilde oluşturulduğunu, birim ve enstrümantasyon testlerini geçtiğini ve gerekli tüm giriş noktalarını koruduğunu doğrulayın.

Modül oluşturma ve test etme

  1. R8 ve AAPT2'nin eksik referans uyarıları olmadan tamamlandığını doğrulamak için modülü temiz bir şekilde oluşturun:

    m <MODULE_NAME>
    
  2. atest kullanarak modül için birim ve enstrümantasyon testleri çalıştırın:

    atest <TEST_MODULE_NAME>
    
  3. Sistem uygulamaları, ayrıcalıklı hizmetler veya donanıma bağımlı modüller için hedef fiziksel cihazlarda ya da cihaz test laboratuvarınızda enstrümantasyon ve kullanıcı arayüzü testleri çalıştırarak çalışma zamanı yansıtma yollarını, IPC bağlamalarını ve kaynak genişletmeyi kullanın.

DEX ve kaynak farklılıklarını inceleme

R8'in, beklenen giriş noktalarını kaldırmadan kullanılmayan kodu ve kaynakları kaldırdığını doğrulamak için derlenmiş APK veya JAR'ı kural değişikliklerinden önce ve sonra karşılaştırın:

  • Çıkış APK veya DEX dosyalarındaki tutulan sınıfları, yöntemleri ve alanları incelemek için apkanalyzer veya dexdump tuşlarını kullanın:

    apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    
  • keep.xml değiştirilirken veya shrink_resources etkinleştirilirken, derlemenin kullanılmayan kaynakları temizlediğini ve gerekli olanları koruduğunu doğrulamak için aapt2 dump resources kullanın:

    aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    

R8 ile saklama yarıçapını ve kural kapsamasını analiz etme

Açık kaynaklı R8 derleyicisi, derleme sırasında her saklama kuralının tam etkisini ölçen bir Saklama Yarıçapı analiz aracı içerir (Android Studio ve Gradle'da R8 Yapılandırma Analiz Aracı olarak da sunulur). Soong, bu analiz aracı doğrudan Android platform derlemesine entegre eder (build/soong/java/dex.go içinde yapılandırılır).

Bir platform modülündeki veya tüm derlemedeki saklama kurallarını analiz etmek için:

  1. Derlemeyi R8_DUMP_KEEP_RADIUS=true ortam değişkeniyle çalıştırın:

    R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>
    

    R8_DUMP_KEEP_RADIUS=true ayarlandığında Soong, R8'e out/soong/.intermediates/ altında derlenen her modül için ara r8keepradius.pb dosyasına keep rule metriklerini kaydetmesini söyler. R8, her saklama kuralı ve keepanno ek açıklaması için şunları kaydeder:

    • Hemen koruma yarıçapı: Kural tarafından korunan sınıflar, alanlar ve yöntemler ile küçültme, optimizasyon veya karartmaya karşı uyguladığı kısıtlamalar.
    • Kural kapsama: Hangi diğer saklama kuralları veya ek açıklamalar aynı öğeleri zaten saklıyor? Özel bir kural tamamen bir AAPT2 kuralı veya genel temel kural tarafından kapsanıyorsa bu kuralı güvenle silebilirsiniz.
    • Paket genelinde ve genel kurallar: Paket genelinde geniş joker karakterler kullanan veya genel yapılandırma yönergeleri uygulayan kurallar.
  2. r8keepradius.pb çıktısını KeepRadiusHtmlReportGenerator (prebuilts/r8/r8.jar içinde paketlenmiş) kullanarak etkileşimli bir HTML raporuna dönüştürün:

    # Generate an HTML report for a single module
    INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME>
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        $INTERMEDIATES/android_common/r8keepradius.pb \
        keep_radius_report.html
    
    # Scan all built modules under out/soong/.intermediates and generate
    # per-module HTML reports plus an aggregate keepradius.html summary
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        out/soong/.intermediates \
        out/keep_radius_reports
    

    Bir dizin verildiğinde KeepRadiusHtmlReportGenerator tüm dosyaları *keepradius*.pb tarar, her modül için bir HTML raporu oluşturur ve out/keep_radius_reports/keepradius.html özetleyen bir rapor oluşturur. Bu raporda, derleme genelinde canlı öğe sayıları, saklanan öğe sayıları ve en yüksek yarıçaplı saklama kuralları yer alır. Oluşturulan rapordaki küçültme, optimizasyon ve karartma puanlarını yorumlama hakkında daha fazla bilgi için R8 Yapılandırma Analiz Aracı'nı kullanma başlıklı makaleyi inceleyin.

Soong, build/soong/java/dex.go içinde iki ek R8 teşhis ortam değişkenini de destekler:

  • R8_DUMP_INPUT=true: Bağımsız R8 yeniden üretimi için tüm giriş JAR'larını, kitaplık JAR'larını ve birleştirilmiş ProGuard yapılandırmalarını içeren modülün ara dizinine r8inputs.zip yazar.
  • R8_DUMP_PERFETTO_TRACE=true: Perfetto'da R8 derleme geçişlerini incelemek için modülün r8trace.ptrace ara dizinine yazar.

Kitaplık tüketici kurallarını doğrulama

Bir java_library veya android_library dışa aktarma işlemi, kuralları aşağı akış tüketicilerine (export_proguard_flags_files: true) aktardığında bu kurallar, tüketen uygulama için optimizasyonu değiştiren veya devre dışı bırakan genel işaretler içermemelidir.

R8, Android platform derlemesinde process-keep-rules ana makine aracı olarak kullanıma sunulan (prebuilts/r8/Android.bp içinde tanımlanır) açık kaynaklı bir saklama kuralı analiz aracı (ProcessKeepRules) sağlar:

# Build the R8 keep rules validator host binary
m process-keep-rules

# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>

process-keep-rules aracı, her yapılandırma dosyasını ayrıştırır ve kitaplık tüketicisi kurallarında izin verilmeyen yönergelerle karşılaşırsa dosya ve satır teşhisleriyle başarısız olur. İzin verilmeyen yönergeler arasında genel optimizasyon, küçültme veya karartma devre dışı bırakma (ör. -dontoptimize veya -dontshrink), paket yeniden paketleme ve erişim değiştirme işaretleri, teşhis veya eşleme işaretleri ve uygulama düzeyinde -keepattributes yer alır.

pgaudit.py ile kaynak ağaçlarını denetleme

Android platform ağacı, R8'in derleme zamanı analiz araçlarıyla birlikte oluşturulmamış kaynak dizinleri denetlemek için build/make/core/proguard/tools/ altında pgaudit.py komut dosyasını içerir. Bu statik analiz aracı, Android.bp, proguard.flags ve keep.xml dosyalarını bir depoda tarayarak AAPT2 veya genel platform temelleri, geniş joker karakterler, -dontoptimize geçersiz kılmaları ve yalnızca test için saklama kuralları tarafından halihazırda kapsanan kuralları işaretler:

# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/

# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json

Platform ve OEM ekipleri, hızlı kaynak ağacı triyajı için pgaudit.py, dışa aktarılan kitaplık kurallarını doğrulamak için process-keep-rules ve bir derlemedeki her kuralın tam sınıf, alan ve yöntem saklama yarıçapını ölçmek için R8_DUMP_KEEP_RADIUS=true ile KeepRadiusHtmlReportGenerator'ü birleştirebilir.