Mengoptimalkan kode dan resource platform dengan R8

Sistem build platform Android (Soong) menjalankan compiler R8 pada target yang mengompilasi bytecode DEX, termasuk aplikasi Android (android_app), pengujian (android_test), dan library Java yang dapat diinstal dengan compile_dex: true (seperti services.jar), untuk mengecilkan, mengoptimalkan, dan menghapus kode dan resource yang tidak digunakan. Pada library statis (android_library dan java_library statis), Soong tidak menjalankan R8 secara langsung, tetapi menggunakan blok properti optimize untuk melampirkan dan menyebarkan aturan keep konsumen (export_proguard_flags_files: true) ke target DEX hilir yang menautkannya secara statis.

Untuk engineer platform yang membuat image sistem atau paket vendor, menghilangkan aturan penyimpanan yang terlalu luas memberikan manfaat kesehatan sistem langsung:

  • Jejak partisi sistem yang lebih kecil: R8 menghapus class, metode, dan resource yang tidak digunakan sebelum mengemas APK dan JAR ke /system, /system_ext, /product, dan /vendor.
  • Artefak yang dikompilasi lebih kecil: Metode DEX yang lebih sedikit berarti file .odex dan .vdex yang dihasilkan oleh dex2oat selama kompilasi waktu build atau di perangkat lebih kecil.
  • Tekanan memori runtime yang lebih rendah: Karena Android memetakan .odex, .vdex, dan tabel resource APK ke memori proses menggunakan panggilan mmap yang dipaginasi sesuai permintaan, biner yang lebih kecil mengurangi kesalahan halaman utama selama startup aplikasi dan menurunkan memori kode dan resource residen di seluruh proses. Untuk mengetahui informasi selengkapnya tentang pengaruh kode aplikasi yang dikompilasi terhadap memori perangkat, lihat Kode aplikasi adalah memori.
  • Pengoptimalan seluruh program yang lebih efektif: Batasan keep yang sempit memungkinkan R8 menyisipkan metode, melakukan devirtualisasi antarmuka dan panggilan virtual, memangkas kolom yang tidak digunakan, dan menyebarkan konstanta di seluruh class.

Mengonfigurasi pengoptimalan R8 di Soong

Dalam file Android.bp, konfigurasi pengoptimalan R8 menggunakan blok properti optimize pada android_app dan target java_library yang dapat diinstal (atau pada android_library dan modul java_library statis untuk mengekspor aturan keep konsumen):

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

Tabel berikut merangkum properti optimize yang paling umum di Soong (ditentukan dalam build/soong/java/dex.go):

Properti Deskripsi
enabled Mengontrol apakah R8 berjalan di target. Nilai defaultnya adalah true untuk semua target android_app.
shrink Mengontrol tree shaking untuk menghapus class, kolom, dan metode yang tidak dapat dijangkau. Nilai defaultnya adalah true untuk target android_app (false untuk java_library mandiri dan modul pengujian, yang meneruskan -dontshrink kecuali jika ditetapkan secara eksplisit).
optimize Mengontrol pengoptimalan bytecode seperti penyisipan metode, penggabungan class, propagasi konstanta, dan penghapusan cabang tidak terpakai. Nilai defaultnya adalah true untuk target android_app (dikontrol oleh flag build rilis RELEASE_R8_OPTIMIZE_BY_DEFAULT).
obfuscate Mengontrol pengecilan dan penggantian nama ID. Secara default, nilainya adalah false untuk kompatibilitas historis, tetapi tetapkan ke true jika memungkinkan untuk mengurangi ukuran DEX dan mengaktifkan pengoptimalan yang lebih mendalam (lihat Aktifkan obfuscation jika memungkinkan).
shrink_resources Menghapus resource yang tidak digunakan (entri res/) dari APK yang dipaketkan setelah penyingkatan kode. Nilai defaultnya adalah false.
optimized_shrink_resources Menjalankan pipeline penyingkat kode dan resource R8 terintegrasi sehingga R8 melacak referensi kode dan resource secara bersamaan dalam satu proses. Jika shrink_resources: true ditetapkan, defaultnya adalah RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true dalam build platform standar).
proguard_flags_files Mencantumkan file .flags atau .pro khusus modul yang berisi aturan penyimpanan kustom. Hindari menambahkan file kustom jika anotasi atau default standar sudah cukup.
export_proguard_flags_files Menyebarkan proguard_flags_files library ini ke modul downstream yang bergantung padanya secara statis.
trace_references_from Mencantumkan target pendamping library Java yang bytecode-nya merujuk ke target ini yang secara otomatis dilacak dan dipertahankan oleh R8.

Aktifkan pengaburan jika memungkinkan

Di Soong, obfuscate secara default adalah false untuk kompatibilitas historis, tetapi Anda harus menetapkan obfuscate: true secara eksplisit pada android_app dan target DEX mandiri jika memungkinkan:

  • Jejak DEX dan .vdex yang lebih kecil: Mengganti nama paket, class, kolom, dan metode menjadi ID pendek (a, b) akan mengecilkan kumpulan string DEX (string_ids dan string_data_item) serta deskriptor jenis. Karena Android memetakan file .vdex dan .odex ke dalam memori proses, tabel simbol yang lebih kecil secara langsung mengurangi ukuran partisi sistem dan jejak memori runtime.
  • Pengoptimalan seluruh program yang lebih mendalam: Dengan mengizinkan R8 mengganti nama ID, penggabungan class, perataan paket, dan penghapusan duplikat class sintetis dapat dilakukan di seluruh paket yang seharusnya dilewati R8 untuk menghindari konflik nama.
  • Simbolisasi stack trace lengkap: Mengaktifkan pengaburan di Soong tidak mengurangi kemampuan proses debug. Untuk setiap target yang dikompilasi R8, Soong akan memancarkan file pemetaan proguard_dictionary di direktori perantara modul, menggabungkan semua kamus modul ke dalam artefak proguard-dict.zip build, menyematkan hash pemetaan (--map-id-template) ke dalam header DEX, dan menulis ulang atribut class SourceFile (--source-file-template) sehingga retrace dapat menyimbolkan stack trace secara otomatis.

Pertahankan obfuscate: false (atau pertahankan nama API publik secara eksplisit) hanya untuk:

  • Library bersama di bootclasspath atau classpath system_server: Modul (java_library atau java_sdk_library) yang mengekspos API eksternal yang ditautkan secara dinamis oleh modul lain saat runtime (seperti target framework.jar, services.jar, atau <uses-library>) harus mempertahankan obfuscate: false atau secara eksplisit mempertahankan permukaan API-nya menggunakan aturan @KeepForApi atau -keep (bersama dengan protect_api_surface: true untuk target bootclasspath) sehingga pemanggil yang dikompilasi terhadap stub-nya dapat menyelesaikan nama class dan anggota saat runtime.

Sebelum mengaktifkan obfuscate: true di aplikasi, verifikasi prasyarat migrasi berikut:

  • Dependensi APK pengujian eksternal: Jika APK android_test eksternal menetapkan instrumentation_for: "MyApp" dan memanggil secara langsung class atau metode internal MyApp, mengganti nama simbol tersebut akan menyebabkan NoSuchMethodError atau NoClassDefFoundError saat runtime pengujian. Sebaiknya susun pengujian sebagai APK yang menginstrumentasikan sendiri yang menautkan MyApp.impl secara statis, anotasi hook pengujian dengan @VisibleForTesting, atau konfigurasi trace_references_from (lihat Langkah 2: Memigrasikan aturan penyimpanan berbasis pengujian) sehingga simbol internal yang diperlukan oleh pengujian dipertahankan sekaligus meng-obfuscate aplikasi lainnya.
  • Refleksi dan JNI berbasis string: Jika modul mencari class, metode, atau kolom berdasarkan nama string literal (Class.forName, getDeclaredMethod, atau JNI FindClass dan GetMethodID) tanpa aturan atau anotasi keep, pengaburan akan mengganti nama target tersebut dan merusak pencarian runtime. (Perhatikan bahwa refleksi yang tidak diberi anotasi juga gagal di shrink: true dan optimize: true.) Beri anotasi pada titik entri tersebut dengan Panduan untuk Mempertahankan Anotasi (@UsesReflection, @UsedByReflection, atau @UsedByNative) agar R8 mempertahankan namanya dan meng-obfuscate modul lainnya.

Mengikuti prinsip nol tanda kustom

Modul platform Android yang ideal tidak memiliki file proguard.flags atau keep.xml kustom. Dalam build platform Android, sebagian besar aturan penyimpanan kustom bersifat redundan:

  • Dasar platform dan AAPT2: Sebagian besar titik entri sudah dipertahankan secara otomatis oleh dasar platform global (seperti @Keep, metode JNI native, dan @VisibleForTesting) atau dibuat oleh AAPT2 dari AndroidManifest.xml dan resource tata letak (lihat Memahami aturan keep platform default).
  • Alternatif yang ditargetkan: Jika titik entri harus dipertahankan, sebaiknya gunakan anotasi situs deklarasi (keepanno, @VisibleForTesting), aturan library yang diekspor (export_proguard_flags_files: true), atau penautan pengujian statis daripada file .flags yang terlepas (lihat Mengaudit dan memigrasikan aturan penyimpanan yang ada).

Sebelum menambahkan atau mempertahankan file proguard.flags atau keep.xml kustom, verifikasi apakah aturan sudah ditangani oleh dasar pengukuran default atau dapat dimigrasikan ke anotasi kode.

Memahami aturan penyimpanan platform default

Soong secara otomatis meneruskan aturan dasar global berikut ke setiap pemanggilan R8 pada target kompilasi DEX (android_app, android_test, dan modul java_library yang dapat diinstal), yang dikonfigurasi di build/soong/java/dex.go:

  • build/make/core/proguard.flags: Mempertahankan class dan anggota yang diberi anotasi dengan @com.android.internal.annotations.VisibleForTesting di semua paket, dan dengan @VisibleForTesting (androidx.annotation.VisibleForTesting atau com.google.common.annotations.VisibleForTesting) dalam paket android.**, com.android.**, dan com.google.android.**. Juga mempertahankan @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback, dan @dalvik.annotation.optimization.**.
  • build/make/core/proguard_basic_keeps.flags: Mempertahankan atribut SourceFile untuk stack trace, anotasi visibilitas runtime (RuntimeVisible*Annotations), atribut Exceptions dan AnnotationDefault, metode native, anggota Serializable, metode @JavascriptInterface, konstruktor Throwable(String), kolom Parcelable$CREATOR, dan kolom protobuf MessageLite.
  • build/make/core/proguard/kotlin.flags: Membisukan peringatan tidak berbahaya untuk meta-anotasi Kotlin tertentu (kotlin.Metadata dan kotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) serta menghapus anotasi DebugMetadata Kotlin dalam build rilis.
  • build/make/core/proguard/checknotnull.flags: Menggantikan panggilan helper pemeriksaan null umum (com.google.common.base.Preconditions.checkNotNull dan dagger.internal.Preconditions.checkNotNull*) dengan pemeriksaan null bytecode yang ringkas. File ini sengaja menghilangkan Objects.requireNonNull untuk mempertahankan pesan pengecualian eksplisit di seluruh batas API framework.
  • build/make/core/proguard/enumvalues.flags: Mempertahankan metode values dan valueOf pada jenis enum kecuali jika dinonaktifkan oleh konfigurasi build.
  • Aturan yang dibuat otomatis oleh AAPT2: AAPT2 memeriksa file AndroidManifest.xml, XML tata letak, dan XML preferensi yang digabungkan untuk membuat aturan keep yang tepat untuk setiap Activity, Service, BroadcastReceiver, ContentProvider, BackupAgent, Application, subclass View kustom, subclass Preference, dan metode android:onClick XML yang terdaftar.

Mengaudit dan memigrasikan aturan penyimpanan yang ada

Saat mengaudit file proguard.flags atau keep.xml yang ada di repositori platform, evaluasi setiap aturan secara berurutan berdasarkan hierarki empat langkah berikut:

Langkah 1: Hapus aturan yang berlebihan atau sudah tidak berlaku

Hapus aturan yang sudah tercakup oleh dasar global, aturan tata letak dan manifes AAPT2, atau default R8, serta aturan yang mereferensikan class atau paket yang tidak ada lagi.

Jika semua aturan dalam file .flags bersifat redundan, hapus file dan hapus proguard_flags_files dari Android.bp. Jika blok optimize yang tersisa hanya menyatakan kembali setelan default, hapus blok yang berlebihan dan format file build dengan bpfmt -w Android.bp. Saat membersihkan paket, periksa apakah modul pendamping di subdirektori (seperti varian target Kotlin) mereferensikan file tanda yang sama dan perbarui secara bersamaan.

Langkah 2: Migrasikan aturan penyimpanan berbasis pengujian

Pindahkan aturan penyimpanan berbasis pengujian dari file produksi kustom .flags menggunakan salah satu pola berikut:

  • Tautkan library penerapan ke dalam pengujian (static_libs): Jika android_test menggunakan detail penerapan internal atau khusus paket aplikasi, arsitektur platform yang direkomendasikan adalah menempatkan file sumber aplikasi di android_library (MyApp.impl) dan menautkan MyApp.impl secara statis ke MyApp dan MyAppTests:

    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"],
    }
    

    Pola 3 modul ini memungkinkan MyAppTests berjalan sebagai pengujian yang menginstrumentasikan diri sendiri dengan akses penuh ke class internal, memungkinkan MyApp mengaktifkan obfuscate: true secara bebas tanpa merusak pengujian, menghindari pengiriman titik entri khusus pengujian dalam APK produksi, dan menghilangkan pembangunan ulang MyApp saat kode pengujian berubah.

  • Anotasi hook pengujian dengan @VisibleForTesting: Jika APK pengujian eksternal memanggil sejumlah kecil metode atau konstruktor internal pada target android_app dan penataan ulang ke dalam library .impl tidak praktis, anotasi deklarasi tersebut dengan @VisibleForTesting. Dasar pengukuran global build/make/core/proguard.flags mempertahankan item @VisibleForTesting dalam paket android.**, com.android.**, dan com.google.android.** secara otomatis tanpa file .flags kustom. (Untuk modul vendor di luar namespace ini, gunakan @UsedByReflection atau aturan library yang diekspor.)

  • Menggunakan trace_references_from di seluruh batas library platform: Jika pengujian tidak dapat menautkan implementasi target secara statis (misalnya, pengujian yang menggunakan layanan server sistem atau JAR platform seperti framework-connectivity), konfigurasi trace_references_from pada target modul yang mengarah ke modul pendamping library Java yang berisi sumber pengujian. R8 melacak dan mempertahankan semua class dan anggota yang dirujuk oleh bytecode pendamping.

Langkah 3: Mengekspor aturan dari pustaka pemilik

Jika java_library atau android_library bersama memerlukan aturan penyimpanan untuk callback JNI atau refleksi internalnya sendiri, tentukan aturan pada target library dan tetapkan export_proguard_flags_files: true:

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

Semua target android_app dan library downstream yang menautkan my-shared-library secara statis akan mewarisi aturan tersebut secara otomatis, sehingga aplikasi downstream tidak perlu menduplikasinya. Jika beberapa modul di berbagai direktori harus berbagi set aturan yang tidak terikat ke satu library kode, publikasikan file .flags melalui filegroup eksplisit di Android.bp sehingga modul dapat mereferensikan :my-shared-flags dengan bersih di seluruh batas paket. Untuk panduan umum tentang menulis aturan penyimpanan konsumen library tanpa membatasi pengoptimalan aplikasi hilir, lihat Pengoptimalan untuk penulis library.

Langkah 4: Anotasi deklarasi dalam kode sumber

Jika class, metode, konstruktor, atau kolom diakses melalui refleksi atau JNI dan tidak tercakup oleh AAPT2 atau tolok ukur global, ganti aturan -keep yang terlepas dalam file .flags dengan anotasi sumber dari Panduan untuk Mempertahankan Anotasi (lihat referensi Javadoc keepanno):

  • @UsesReflection: Pilih anotasi ini jika Anda mengontrol kode yang melakukan refleksi. Tempatkan di situs panggilan refleksi untuk mendeklarasikan class, metode, atau kolom target mana yang diakses secara dinamis. Karena @UsesReflection otomatis mengenkode prasyarat bahwa situs panggilan yang dianotasi itu sendiri dapat dijangkau, R8 akan memangkas pemanggil dan target reflektif jika situs panggilan tidak digunakan.
  • @UsedByReflection: Tempatkan pada class, metode, kolom, atau konstruktor yang di-instansiasi atau dipanggil secara reflektif oleh kode atau library eksternal, seperti class yang dimuat dengan Class.forName dari kunci Bundle atau Settings, atau class plugin yang dimuat di seluruh classloader dinamis. Tentukan kind (seperti KeepItemKind.CLASS_AND_METHODS), preconditions (@KeepCondition), dan batasan parameter sehingga R8 hanya menyimpan kontrak reflektif yang tepat dan masih dapat mengoptimalkan atau memangkas anggota class yang tidak digunakan. Jangan menambahkan @UsedByReflection ke komponen yang terdaftar di manifes seperti JobService atau BroadcastReceiver, karena AAPT2 sudah menyimpannya.
  • @UsedByNative: Tempatkan pada metode atau kolom yang diakses dari kode JNI C atau C++ menggunakan GetMethodID, GetStaticMethodID, atau GetFieldID.
  • @KeepForApi: Tempatkan pada class atau anggota API library yang harus tetap utuh saat library itu sendiri di-shrink sebelum didistribusikan.
  • @Keep (androidx.annotation.Keep): Gunakan sebagai penggantian jika keepanno tidak berlaku. Menerapkan @Keep ke suatu class akan mempertahankan class dan semua anggotanya tanpa syarat. Menerapkan @Keep ke metode atau kolom bertindak sebagai titik entri tanpa syarat yang mempertahankan anggota dan class yang memuatnya meskipun class tidak pernah di-instance, sedangkan keepanno mengekspresikan keterjangkauan bersyarat.

Untuk menggunakan anotasi keepanno R8 dalam modul Soong:

  1. Tambahkan "keepanno-annotations" ke libs di Android.bp:

    libs: [
        "keepanno-annotations",
    ],
    
  2. Impor com.android.tools.r8.keepanno.annotations.* dan anotasi deklarasi atau situs panggilan dalam kode sumber Java atau Kotlin:

    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 menerjemahkan anotasi keepanno langsung ke dalam model aturan keep internalnya dan menghapus anotasi dari output DEX akhir, sehingga tidak menambahkan overhead bytecode runtime.

Menghindari kesalahan umum

Bagian berikut menjelaskan kesalahan umum proguard.flags dan keep.xml dalam kode platform dan cara memperbaikinya.

Aturan penyimpanan komponen luas

# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
  • Alasan aturan ini menurunkan performa: Aturan ini sepenuhnya berlebihan dengan AAPT2. Karena menggunakan karakter pengganti tanpa memeriksa apakah komponen terdaftar di AndroidManifest.xml gabungan akhir, R8 dipaksa untuk mempertahankan setiap subclass Activity, Service, atau BroadcastReceiver yang ditemukan di mana pun di classpath, termasuk komponen library yang tidak digunakan dan aktivitas debug yang dinonaktifkan.
  • Perbaikan yang direkomendasikan: Hapus aturan. AAPT2 memeriksa manifes gabungan dan membuat aturan -keep yang tepat untuk komponen terdaftar.

Aturan penyimpanan handler klik XML luas

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • Alasan performa terpengaruh: Mempertahankan setiap metode public void *(View) di setiap class dalam modul mencegah R8 menghapus atau menyisipkan metode apa pun yang menerima satu parameter View.
  • Perbaikan yang direkomendasikan: Hapus aturan. AAPT2 memindai file XML tata letak dan membuat aturan keep yang ditargetkan untuk metode yang dirujuk oleh atribut android:onClick.

Aturan penyimpanan getter dan setter Broad View

# 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*();
}
  • Alasan performa terpengaruh: Aturan ini memaksa R8 untuk mempertahankan setiap getter dan setter di setiap subclass View dalam aplikasi dan semua library tertaut (termasuk library AndroidX dan Material), sehingga memblokir penghapusan metode tidak terpakai dan inlining di seluruh kode UI.
  • Perbaikan yang direkomendasikan: Hapus aturan. AAPT2 sudah mempertahankan konstruktor untuk class View kustom yang di-inflate dari file XML tata letak. Jika kode menganimasikan properti tampilan menggunakan nama string reflektif seperti ObjectAnimator.ofFloat(view, "translationZ", ...), ganti nama string dengan referensi properti yang diketik (View.TRANSLATION_Z atau implementasi FloatProperty atau IntProperty kustom) untuk menghindari refleksi sepenuhnya. Jika akses properti reflektif tidak dapat dihindari, beri anotasi pada getter atau setter tertentu dengan @UsedByReflection.

Pengoptimalan atau pengaburan menyeluruh dinonaktifkan

# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
  • Alasan performa terpengaruh: Menempatkan -dontoptimize atau -dontshrink di dalam file .flags secara diam-diam akan menggantikan setelan Android.bp modul dan menonaktifkan pengoptimalan di seluruh target. Lebih buruk lagi, jika library mengekspor file .flags yang berisi -dontoptimize, hal ini akan menonaktifkan pengoptimalan R8 untuk setiap android_app hilir yang menautkan library.
  • Perbaikan yang direkomendasikan: Hapus -dontoptimize, -dontshrink, dan -dontobfuscate dari file .flags. Mengontrol perilaku pengoptimalan secara eksplisit di Android.bp menggunakan blok optimize (shrink, optimize, obfuscate) pada target daun.

Tanda diagnostik dalam aturan yang diterapkan

# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
  • Alasan merusak build: Flag diagnostik membanjiri log build atau mencoba menulis file output ke jalur lokal selama build Soong yang di-sandbox.
  • Perbaikan yang direkomendasikan: Hapus tanda ini dari file .flags yang di-commit. Soong secara otomatis menulis output penggunaan dan pemetaan R8, seperti proguard_dictionary dan proguard_usage.zip, ke direktori perantara modul di bagian out/soong/.intermediates/.

Aturan penyimpanan produksi untuk kode pengujian

  • Alasan penurunan performa: Menambahkan aturan -keep kustom hanya untuk akses pengujian memaksa kode tersebut tetap tidak di-obfuscate dan dipertahankan dalam build produksi. Meskipun menganotasi metode dengan @VisibleForTesting atau mengonfigurasi trace_references_from menghindari pemeliharaan aturan -keep manual, simbol tersebut tetap dikirim dalam biner produksi.
  • Perbaikan yang direkomendasikan: Untuk menjaga agar titik entri khusus pengujian benar-benar tidak ada di APK produksi, susun aplikasi ke dalam library .impl dan tautkan ke APK pengujian yang melakukan instrumentasi sendiri menggunakan static_libs, seperti yang dijelaskan dalam Langkah 2: Memigrasikan aturan keep berbasis pengujian.

Karakter pengganti resource menyeluruh di keep.xml

<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@raw/*,@drawable/*,@string/*" />
  • Alasan performa terpengaruh: Kartu pengganti menyeluruh mempertahankan setiap resource dalam jenis yang cocok di seluruh modul dan dependensinya, sehingga mengalahkan pengecilan resource (shrink_resources: true) dan memperbesar APK serta tabel resources.arsc yang dipetakan.
  • Perbaikan yang direkomendasikan:
    • Lebih memilih referensi resource statis daripada keep.xml: Hindari penggunaan metode Resources.getIdentifier untuk mencari kumpulan resource yang terikat secara dinamis, seperti string eksperimen bernomor atau drawable bertema. Sebagai gantinya, gunakan pernyataan switch waktu kompilasi atau petakan konstanta statis R.id, R.string, atau R.drawable. Referensi statis memungkinkan R8 dan AAPT2 melacak resource live yang tepat, menghilangkan overhead pencarian string runtime, dan menghilangkan kebutuhan akan keep.xml sama sekali.
    • Mencantumkan nama resource tertentu: Jika diperlukan pencarian dinamis, seperti oleh pembaca lisensi pihak ketiga, cantumkan ID resource yang tepat di tools:keep (misalnya, tools:keep="@raw/third_party_licenses").
    • Hapus file keep.xml yang berlebihan: Jika resource yang tercantum sudah direferensikan secara statis dalam kode (R.raw.foo) atau XML (@raw/foo), alat penyingkat akan otomatis mempertahankannya, sehingga Anda dapat menghapus res/raw/keep.xml.

Memverifikasi dan mengaudit perubahan aturan

Setiap kali Anda menghapus atau mempersempit aturan penyimpanan di proguard.flags atau keep.xml, verifikasi bahwa modul dibangun dengan bersih, lulus pengujian unit dan instrumentasi, serta mempertahankan semua titik entri yang diperlukan.

Membangun dan menguji modul

  1. Bangun modul dengan bersih untuk memverifikasi bahwa R8 dan AAPT2 selesai tanpa peringatan referensi yang hilang:

    m <MODULE_NAME>
    
  2. Jalankan pengujian unit dan instrumentasi untuk modul menggunakan atest:

    atest <TEST_MODULE_NAME>
    
  3. Untuk aplikasi sistem, layanan istimewa, atau modul yang bergantung pada hardware, jalankan pengujian instrumentasi dan UI pada perangkat fisik target atau di lab pengujian perangkat Anda untuk melatih jalur refleksi runtime, binding IPC, dan inflasi resource.

Memeriksa perbedaan DEX dan resource

Bandingkan APK atau JAR yang dikompilasi sebelum dan sesudah perubahan aturan untuk mengonfirmasi bahwa R8 menghapus kode dan resource yang tidak digunakan tanpa menghapus titik entri yang diharapkan:

  • Gunakan apkanalyzer atau dexdump untuk memeriksa class, metode, dan kolom yang dipertahankan dalam file APK atau DEX output:

    apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    
  • Saat mengubah keep.xml atau mengaktifkan shrink_resources, gunakan aapt2 dump resources untuk memverifikasi bahwa build menghapus resource yang tidak digunakan dan mempertahankan resource yang diperlukan:

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

Menganalisis radius keep dan subsumsi aturan dengan R8

Compiler R8 open source menyertakan penganalisis Keep Radius (juga diekspos di Android Studio dan Gradle sebagai R8 Configuration Analyzer) yang mengukur dampak persis setiap aturan keep selama kompilasi. Soong mengintegrasikan penganalisis ini langsung ke build platform Android (dikonfigurasi di build/soong/java/dex.go).

Untuk menganalisis aturan penyimpanan untuk modul platform atau di seluruh build:

  1. Jalankan build dengan variabel lingkungan R8_DUMP_KEEP_RADIUS=true:

    R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>
    

    Jika R8_DUMP_KEEP_RADIUS=true disetel, Soong akan menginstruksikan R8 untuk merekam metrik aturan keep dalam file r8keepradius.pb perantara untuk setiap modul yang dikompilasi di out/soong/.intermediates/. Untuk setiap aturan keep dan anotasi keepanno, R8 mencatat:

    • Radius penyimpanan langsung: Class, kolom, dan metode persis yang dipertahankan oleh aturan, beserta batasan spesifik yang diterapkan terhadap penyusutan, pengoptimalan, atau pengaburan.
    • Pencakupan aturan: Aturan atau anotasi penyimpanan mana yang sudah mempertahankan item yang sama. Jika aturan kustom sepenuhnya tercakup oleh aturan AAPT2 atau aturan dasar global, Anda dapat menghapusnya dengan aman.
    • Aturan di seluruh paket dan global: Aturan yang menggunakan karakter pengganti luas di seluruh paket atau menerapkan direktif konfigurasi global.
  2. Konversi output r8keepradius.pb menjadi laporan HTML interaktif menggunakan KeepRadiusHtmlReportGenerator (digabungkan dalam prebuilts/r8/r8.jar):

    # 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
    

    Jika diberi direktori, KeepRadiusHtmlReportGenerator akan menelusuri semua file *keepradius*.pb, membuat laporan HTML untuk setiap modul, dan membuat out/keep_radius_reports/keepradius.html yang merangkum jumlah item aktif, jumlah item yang dipertahankan, dan aturan pertahankan dengan radius tertinggi di seluruh build. Untuk mengetahui informasi selengkapnya tentang cara menafsirkan skor penyusutan, pengoptimalan, dan peng-obfuscate dalam laporan yang dihasilkan, lihat Menggunakan R8 Configuration Analyzer.

Soong juga mendukung dua variabel lingkungan diagnostik R8 tambahan di build/soong/java/dex.go:

  • R8_DUMP_INPUT=true: Menulis r8inputs.zip ke direktori perantara modul yang berisi semua JAR input, JAR library, dan konfigurasi ProGuard yang digabungkan untuk reproduksi R8 mandiri.
  • R8_DUMP_PERFETTO_TRACE=true: Menulis r8trace.ptrace ke direktori perantara modul untuk memeriksa langkah kompilasi R8 di Perfetto.

Memvalidasi aturan konsumen library

Saat ekspor java_library atau android_library menyimpan aturan ke konsumen hilir (export_proguard_flags_files: true), aturan tersebut tidak boleh menyertakan flag global yang mengubah atau menonaktifkan pengoptimalan untuk aplikasi yang menggunakan.

R8 menyediakan penganalisis aturan keep open source (ProcessKeepRules), yang diekspos dalam build platform Android sebagai alat host process-keep-rules (ditetapkan dalam prebuilts/r8/Android.bp):

# 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>

Alat process-keep-rules mengurai setiap file konfigurasi dan gagal dengan diagnostik file dan baris jika menemukan direktif yang tidak diizinkan dalam aturan penggunaan library. Petunjuk yang tidak diizinkan mencakup pengoptimalan global, pengecilan, atau penonaktifan pengaburan (seperti -dontoptimize atau -dontshrink), pengemasan ulang paket dan tanda modifikasi akses, tanda diagnostik atau pemetaan, dan -keepattributes tingkat aplikasi.

Mengaudit pohon sumber dengan pgaudit.py

Untuk mengaudit direktori sumber yang tidak dibangun bersama dengan penganalisis waktu build R8, hierarki platform Android menyertakan skrip pgaudit.py di bagian build/make/core/proguard/tools/. Alat analisis statis ini memindai file Android.bp, proguard.flags, dan keep.xml di seluruh repositori untuk menandai aturan yang sudah dicakup oleh AAPT2 atau dasar platform global, karakter pengganti luas, penggantian -dontoptimize, dan aturan penyimpanan khusus pengujian:

# 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

Tim platform dan OEM dapat menggabungkan pgaudit.py untuk triase pohon sumber yang cepat, process-keep-rules untuk memvalidasi aturan library yang diekspor, dan R8_DUMP_KEEP_RADIUS=true dengan KeepRadiusHtmlReportGenerator untuk mengukur radius retensi class, kolom, dan metode yang tepat dari setiap aturan dalam build.