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 menyingkat, 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 menerapkan
aturan keep konsumen (export_proguard_flags_files: true) ke target DEX
downstream yang menautkannya secara statis.
Bagi engineer platform yang membangun image sistem atau paket vendor, menghilangkan aturan keep yang terlalu luas akan memberikan manfaat langsung bagi kesehatan sistem:
- Jejak partisi sistem yang lebih kecil: R8 menghapus class, metode,
dan resource yang tidak aktif sebelum mengemas APK dan JAR ke
/system,/system_ext,/product, dan/vendor. - Artefak yang dikompilasi lebih kecil: Metode DEX yang lebih sedikit berarti file
.odexdan.vdexyang dihasilkan olehdex2oatselama waktu build atau kompilasi di perangkat akan lebih kecil. - Tekanan memori runtime yang lebih rendah: Karena Android memetakan
.odex,.vdex, dan tabel resource APK ke dalam memori proses menggunakan panggilanmmapdengan halaman 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, konfigurasikan pengoptimalan R8 menggunakan blok properti optimize
pada target android_app dan java_library yang dapat diinstal (atau pada
modul android_library dan 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 pada target. Default-nya adalah true
untuk semua target android_app.
|
shrink |
Mengontrol tree shaking untuk menghapus class, kolom, dan
metode yang tidak dapat dijangkau. Secara default ditetapkan ke 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 inlining metode, penggabungan class,
penyebaran konstanta, dan penghapusan cabang mati. Nilai defaultnya adalah
true untuk target android_app (dikontrol oleh
flag build rilis RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Mengontrol minifikasi dan penggantian nama ID. Defaultnya adalah
false untuk kompatibilitas historis, tetapi tetapkan ke
true jika memungkinkan untuk mengurangi ukuran DEX dan membuka
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 penerusan. Jika
shrink_resources: true ditetapkan, setelan 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 keep kustom. Hindari menambahkan file kustom jika anotasi standar atau default 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 dependen kompilasi DEX langsung (terhubung melalui
instrumentation_for atau libs) yang referensi bytecode-nya
ke target ini akan otomatis dilacak dan dipertahankan oleh R8 (juga
disebarkan di seluruh varian override_android_app dan
override_android_test, serta didukung untuk
library Java pendamping non-dependen jika dipasangkan dengan
pemisahan .impl).
|
allow_traced_references |
Memungkinkan target dependen kompilasi DEX langsung (terhubung melalui
instrumentation_for atau libs, seperti
pengujian lintas repositori atau vendor yang tidak dapat dicantumkan dalam
trace_references_from) untuk memilih ikut serta dalam melacak referensi bytecode-nya
ke target ini dengan menetapkan
trace_references: true.
|
trace_references |
Jika ditetapkan ke true pada dependensi kompilasi DEX langsung dari
target dengan allow_traced_references: true, menjalankan R8
tracereferences pada modul ini dan meneruskan aturan keep
yang dihasilkan ke modul target.
|
protect_api_surface |
Meneruskan --protect-api-surface ke R8. Jika ditetapkan pada
java_sdk_library, juga otomatis membuat dan
melampirkan aturan keep Metalava API di semua cakupan API yang diaktifkan (publik,
sistem, module-lib, server sistem, dan pengujian) ke library
implementasi.
|
Library statis (android_library, java_library, dan impor AAR) juga dapat
menetapkan required_downstream_optimization: { enabled: true, optimize: true } di
Android.bp untuk mewajibkan target kompilasi DEX downstream yang menautkannya
secara statis (langsung atau transitif) untuk mengaktifkan penyingkatan R8 dan pengoptimalan
bytecode. Soong mengaktifkan ini secara otomatis di
androidx.compose.runtime_runtime sehingga target yang menautkan Jetpack Compose
dioptimalkan dengan R8.
Mengaktifkan obfuscation jika memungkinkan
Di Soong, obfuscate ditetapkan secara default ke false untuk kompatibilitas historis, tetapi Anda
harus secara eksplisit menetapkan obfuscate: true pada android_app dan target DEX mandiri
jika memungkinkan:
- Jejak DEX dan
.vdexyang lebih kecil: Mengganti nama paket, class, kolom, dan metode menjadi ID singkat (a,b) akan memperkecil string pool DEX (string_idsdanstring_data_item) dan deskriptor jenis. Karena Android memetakan file.vdexdan.odexke dalam memori proses, tabel simbol yang lebih kecil secara langsung mengurangi ukuran partisi sistem dan jejak memori runtime. - Pengoptimalan seluruh program yang lebih mendalam: Membiarkan R8 mengganti nama ID akan memungkinkan penggabungan class, perataan paket, dan penghapusan duplikat class sintetis di seluruh paket yang harus dilewati R8 untuk menghindari konflik nama.
- Simbolisasi pelacakan tumpukan penuh: Mengaktifkan obfuscation di Soong tidak
mengurangi kemampuan debug. Untuk setiap target yang dikompilasi R8, Soong memunculkan
file pemetaan
proguard_dictionarydi direktori perantara modul, menggabungkan semua kamus modul ke dalam artefakproguard-dict.zipbuild, menyematkan hash pemetaan (--map-id-template) ke dalam header DEX, dan menulis ulang atributSourceFileclass (--source-file-template) sehinggaretracedapat menyimbolkan pelacakan tumpukan secara otomatis.
Pertahankan obfuscate: false (atau pertahankan nama API publik secara eksplisit) hanya untuk:
- Library bersama di bootclasspath atau classpath
system_server: Modul (java_libraryataujava_sdk_library) yang mengekspos platform API eksternal yang ditautkan secara dinamis oleh modul lain pada waktu proses (seperti targetframework.jar,services.jar, atau<uses-library>) harus mempertahankanobfuscate: falseatau secara eksplisit mempertahankan platform API-nya menggunakan aturanprotect_api_surface: true,@KeepForApi, atau-keepsehingga pemanggil yang dikompilasi terhadap stub-nya dapat me-resolve nama class dan anggota pada waktu proses. Pada moduljava_sdk_library, menyetelprotect_api_surface: trueakan otomatis membuat dan melampirkan aturan keep ProGuard Metalava di semua cakupan API yang diaktifkan (publik, sistem, module-lib, system-server, dan pengujian) ke library implementasi. Jika pengoptimalan R8 umum diaktifkan pada target bootclasspath yang tidak dapat diupdate (framework-minus-apex,ext, danframework-location), target tersebut juga menyertakanframework-hiddenapi-proguard-rules(yang dihasilkan olehhiddenapi_to_proguard) untuk mempertahankan anggota yang tercantum dalam fileboot/hiddenapi/*.txtaktif (menghilangkanhiddenapi-max-target-o.txt).
Sebelum mengaktifkan obfuscate: true pada aplikasi, verifikasi prasyarat migrasi berikut:
- Dependensi APK pengujian eksternal: Jika APK
android_testeksternal menetapkaninstrumentation_for: "MyApp"dan langsung memanggil class atau metode internalMyApp, penggantian nama simbol tersebut akan menyebabkanNoSuchMethodErroratauNoClassDefFoundErrorpada waktu pengujian. Konfigurasikantrace_references_from(atauallow_traced_referencesdengantrace_references: true), susun pengujian sebagai APK self-instrumenting yang menautkanMyApp.implsecara statis, atau beri anotasi pada hook pengujian dengan@VisibleForTesting(lihat Langkah 2: Migrasikan aturan keep berbasis pengujian) sehingga simbol internal yang diperlukan oleh pengujian dipertahankan saat meng-obfuscate bagian aplikasi lainnya. - Refleksi berbasis string dan JNI: Jika modul mencari class, metode,
atau kolom berdasarkan nama string literal (
Class.forName,getDeclaredMethod, atau JNIFindClassdanGetMethodID) tanpa aturan keep atau anotasi, obfuscation akan mengganti nama target tersebut dan merusak pencarian runtime. (Perhatikan bahwa refleksi yang tidak dianotasi juga gagal dishrink: truedanoptimize: true.) Beri anotasi pada titik entri tersebut dengan Panduan untuk Mempertahankan Anotasi (@UsesReflection,@UsedByReflection, atau@UsedByNative) sehingga R8 mempertahankan namanya dan meng-obfuscate sisa modul.
Mengikuti prinsip tanpa flag kustom
Modul platform Android yang ideal tidak memiliki file proguard.flags atau keep.xml kustom. Dalam build platform Android, sebagian besar aturan keep kustom bersifat
redundan:
- Baseline platform dan AAPT2: Sebagian besar titik entri sudah dipertahankan
secara otomatis oleh baseline platform global (seperti
@Keep, metode JNInative, dan@VisibleForTesting) atau dibuat oleh AAPT2 dariAndroidManifest.xmldan resource tata letak (lihat Memahami aturan keep platform default). - Alternatif yang ditargetkan: Jika titik entri harus dipertahankan, pilih
anotasi situs deklarasi (
keepanno,@VisibleForTesting), aturan library yang diekspor (export_proguard_flags_files: true), atau penautan pengujian statis daripada file.flagsyang terpisah (lihat Mengaudit dan memigrasikan aturan keep yang ada).
Sebelum menambahkan atau mempertahankan file proguard.flags atau keep.xml kustom, pastikan
apakah aturan tersebut sudah ditangani oleh baseline default atau dapat dimigrasikan ke
anotasi kode.
Memahami aturan penyimpanan platform default
Soong otomatis meneruskan aturan dasar pengukuran 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 dianotasi dengan@com.android.internal.annotations.VisibleForTestingdi semua paket, dan dengan@VisibleForTesting(androidx.annotation.VisibleForTestingataucom.google.common.annotations.VisibleForTesting) dalam paketandroid.**,com.android.**, dancom.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 atributSourceFileuntuk stack trace, anotasi visibilitas runtime (RuntimeVisible*Annotations), atributExceptionsdanAnnotationDefault, metodenative, anggotaSerializable, metode@JavascriptInterface, konstruktorThrowable(String), kolomParcelable$CREATOR, dan kolomMessageLiteprotobuf.build/make/core/proguard/kotlin.flags: Menonaktifkan peringatan yang tidak berbahaya untuk meta-anotasi Kotlin tertentu (kotlin.Metadatadankotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) dan menghapus anotasiDebugMetadataKotlin dalam build rilis.build/make/core/proguard/checknotnull.flags: Mengganti panggilan helper pemeriksaan null umum (com.google.common.base.Preconditions.checkNotNulldandagger.internal.Preconditions.checkNotNull*) dengan pemeriksaan null bytecode yang ringkas. File ini sengaja menghilangkanObjects.requireNonNulluntuk mempertahankan pesan pengecualian eksplisit di seluruh batas API framework.build/make/core/proguard/enumvalues.flags: Mempertahankan metodevaluesdanvalueOfpada jenisenumkecuali jika dinonaktifkan oleh konfigurasi build.- Aturan yang dibuat otomatis oleh AAPT2: AAPT2 memeriksa
AndroidManifest.xml, file XML tata letak, dan file XML preferensi yang digabungkan untuk menghasilkan aturan keep yang tepat untuk setiapActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, subclassViewkustom, subclassPreference, dan metodeandroid:onClickXML 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 redundan atau usang
Hapus aturan yang sudah tercakup dalam dasar pengukuran global, aturan manifes dan tata letak AAPT2, atau default R8, serta aturan yang mereferensikan class atau paket yang sudah tidak ada.
Jika semua aturan dalam file .flags bersifat redundan, hapus file tersebut dan hapus
proguard_flags_files dari Android.bp. Jika blok optimize yang tersisa hanya
menyatakan ulang setelan default, hapus blok yang redundan dan format file build
dengan bpfmt -w Android.bp. Saat membersihkan paket, periksa apakah modul pendamping
di subdirektori (seperti varian target Kotlin) mereferensikan file flag yang sama
dan update bersama-sama.
Langkah 2: Migrasikan aturan keep berbasis pengujian
Pindahkan aturan keep berbasis pengujian dari file .flags produksi kustom menggunakan salah satu
pola berikut:
Lacak referensi dari target pengujian atau pendamping (
trace_references_fromatauallow_traced_references): Jika target dan modul dependen langsung mengompilasi DEX-nya sendiri (android_app,android_test, ataujava_libraryyang di-dex) dan terhubung melaluiinstrumentation_forataulibs, Soong dapat menjalankan R8tracereferencespada modul dependen dan memasukkan aturan keep yang dihasilkan ke dalam kompilasi R8 target tanpa membuat siklus dependensi atau memerlukan pemisahan library.implmanual:Pengujian repositori yang sama (
trace_references_from): Mencantumkan dependensiinstrumentation_forataulibslangsung pada modul target (ini juga otomatis diterapkan di seluruh varianoverride_android_appdanoverride_android_test, serta terus mendukung library Java pendamping yang tidak bergantung saat dipasangkan dengan pemisahan.impl):android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { obfuscate: true, shrink_resources: true, trace_references_from: ["MyAppTests"], }, } android_test { name: "MyAppTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], }Pengujian lintas repositori atau vendor (
allow_traced_referencesdantrace_references): Jika modul target tidak dapat mencantumkan dependensi secara langsung ditrace_references_from(seperti pengujian di repositori atau hierarki vendor lain), tetapkanallow_traced_references: truepada modul target dan ikut serta dari setiap dependensi dengantrace_references: true:android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { allow_traced_references: true, }, } android_test { name: "MyAppVendorTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], optimize: { trace_references: true, }, }Refleksi berbasis string dalam pengujian:
tracereferencesmemeriksa referensi kumpulan konstanta bytecode dan tidak dapat mendeteksi refleksi berbasis string dalam pengujian (Class.forName,getDeclaredField,getDeclaredMethod, atau helper injeksi kolom reflektif). Anotasikan kolom dan metode produksi yang diakses secara reflektif dengan@VisibleForTesting(atau@UsedByReflection).
Menautkan library implementasi ke pengujian (
static_libs): Jika Anda tidak ingin titik entri khusus pengujian dipertahankan di APK produksi dan tidak ingin perubahan pengujian memicu ulang R8 pada target produksi, tempatkan file sumber aplikasi diandroid_library(MyApp.impl) dan tautkanMyApp.implsecara statis keMyAppdanMyAppTests: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
MyAppTestsberjalan sebagai uji instrumentasi mandiri dengan akses penuh ke class internal sekaligus memungkinkanMyAppmenyusutkan dan meng-obfuscate murni terhadap titik entri produksi.Beri anotasi pada hook pengujian dengan
@VisibleForTesting: Jika APK pengujian eksternal hanya memanggil beberapa metode atau konstruktor internal pada targetandroid_app, Anda dapat memberi anotasi pada deklarasi tersebut dengan@VisibleForTesting. Baselinebuild/make/core/proguard.flagsglobal mempertahankan@VisibleForTestingitem dalam paketandroid.**,com.android.**, dancom.google.android.**secara otomatis tanpa file.flagskustom. (Untuk modul vendor di luar namespace ini, gunakantrace_references_from,@UsedByReflection, atau aturan library yang diekspor.) Saat menganotasi secara manual dengan@VisibleForTesting, bukan menggunakantrace_references_fromatautrace_references, anotasikan juga class yang diteruskan ke Mockitomock(...)atauspy(...)(bukan hanya metode stub), karenamockito-target(dexmaker-mockito) di perangkat menghasilkan subclass runtime dan R8 dapat menandai class yang tidak di-subclassfinal(MockitoException: Can't mock/spy class ... - final class).Hindari atau kemas ulang library statis yang tumpang-tindih (
jarjar_rules): Jikaandroid_appdan APK pengujianinstrumentation_for-nya secara statis menautkan library yang sama (secara langsung, sepertiguava, atau secara transitif),tracereferencesakan melihat class tersebut ditentukan dalam pengujian dan tidak memunculkan aturan keep untuk aplikasi, dan pada waktu proses, classloader induk aplikasi akan menyamarkan salinan pengujian dengan class yang disusutkan R8, sehingga menyebabkanIllegalAccessErroratauNoSuchMethodError. Jika pengujian tidak memerlukan salinan paketnya sendiri, hapus duplikat daristatic_libspengujian (atau tautkan melaluilibs) untuk menghindari konflik; jika tidak, kemas ulang paket library statis aplikasi yang tumpang-tindih di bawah awalan khusus modul menggunakanjarjar_rules.
Langkah 3: Ekspor aturan dari koleksi pemilik
Jika java_library atau android_library bersama memerlukan aturan keep untuk refleksi internal atau callback JNI-nya 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 library dan android_app 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 terkait dengan satu library kode, publikasikan file .flags
melalui filegroup eksplisit di Android.bp sehingga modul dapat mereferensikan
:my-shared-flags dengan jelas di seluruh batas paket. Untuk panduan umum tentang
cara menulis aturan keep konsumen library tanpa membatasi pengoptimalan
aplikasi downstream, lihat Pengoptimalan untuk penulis library.
Langkah 4: Beri anotasi pada deklarasi dalam kode sumber
Jika class, metode, konstruktor, atau kolom diakses melalui refleksi atau
JNI dan tidak tercakup oleh AAPT2 atau dasar pengukuran global, ganti aturan -keep
yang terpisah dalam file .flags dengan anotasi sumber dari
Panduan untuk Mempertahankan Anotasi (lihat
referensi Javadoc keepanno):
@UsesReflection: Pilih anotasi ini saat Anda mengontrol kode yang melakukan refleksi. Tempatkan anotasi ini di situs panggilan refleksi untuk mendeklarasikan class, metode, atau kolom target mana yang diakses secara dinamis. Karena@UsesReflectionotomatis 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 dibuat instance-nya atau dipanggil secara reflektif oleh kode atau library eksternal, seperti class yang dimuat denganClass.forNamedari kunciBundleatauSettings, atau class plugin yang dimuat di seluruh classloader dinamis. Tentukankind(sepertiKeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition), dan batasan parameter sehingga R8 hanya mempertahankan kontrak reflektif yang tepat dan masih dapat mengoptimalkan atau memangkas anggota class yang tidak digunakan. Jangan tambahkan@UsedByReflectionke komponen yang terdaftar dalam manifes sepertiJobServiceatauBroadcastReceiver, karena AAPT2 sudah menyimpannya.@UsedByNative: Tempatkan pada metode atau kolom yang diakses dari kode JNI C atau C++ menggunakanGetMethodID,GetStaticMethodID, atauGetFieldID.@KeepForApi: Tempatkan pada class atau anggota API library yang harus tetap utuh saat library itu sendiri diciutkan sebelum didistribusikan.@Keep(androidx.annotation.Keep): Gunakan sebagai pengganti jikakeepannotidak berlaku. Menerapkan@Keepke suatu class akan mempertahankan class tersebut dan semua elemennya tanpa syarat. Menerapkan@Keepke metode atau kolom akan bertindak sebagai titik entri tanpa syarat yang mempertahankan anggota dan class yang memuatnya meskipun class tersebut tidak pernah dibuat instance-nya, sedangkankeepannomengekspresikan keterjangkauan bersyarat.
Untuk menggunakan anotasi keepanno R8 dalam modul Soong:
Tambahkan
"keepanno-annotations"kelibsdiAndroid.bp:libs: [ "keepanno-annotations", ],Impor
com.android.tools.r8.keepanno.annotations.*dan anotasikan 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
keepannolangsung ke 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 keep komponen yang luas
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Alasan hal ini mengganggu performa: Aturan ini sepenuhnya redundan dengan AAPT2.
Karena menggunakan karakter pengganti tanpa memeriksa apakah komponen
terdaftar dalam
AndroidManifest.xmlgabungan akhir, aturan ini memaksa R8 untuk mempertahankan setiap subclassActivity,Service, atauBroadcastReceiveryang ditemukan di mana saja pada classpath, termasuk komponen library yang tidak digunakan dan aktivitas debug yang dinonaktifkan. - Perbaikan yang disarankan: Hapus aturan. AAPT2 memeriksa manifes gabungan dan
membuat aturan
-keepyang tepat untuk komponen terdaftar.
Aturan keep pengendali klik XML yang luas
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Alasan hal ini mengganggu performa: Mempertahankan setiap metode
public void *(View)di setiap class dalam modul akan mencegah R8 menghapus atau menyisipkan metode apa pun yang kebetulan menerima satu parameterView. - Perbaikan yang disarankan: Hapus aturan. AAPT2 memindai file XML tata letak dan
membuat aturan keep yang ditargetkan untuk metode yang direferensikan oleh atribut
android:onClick.
Aturan keep getter dan setter View yang luas
# 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 hal ini menurunkan performa: Aturan ini memaksa R8 untuk mempertahankan setiap pengambil dan
penyetel di setiap subclass
Viewdalam aplikasi dan semua library tertaut (termasuk library AndroidX dan Material), sehingga memblokir penghapusan metode mati dan inlining di seluruh kode UI. - Perbaikan yang disarankan: Hapus aturan. AAPT2 sudah mempertahankan konstruktor
untuk class
Viewkustom yang di-inflate dari file XML tata letak. Jika kode menganimasikan properti tampilan menggunakan nama string reflektif sepertiObjectAnimator.ofFloat(view, "translationZ", ...), ganti nama string dengan referensi properti yang diketik (View.TRANSLATION_Zatau implementasiFloatPropertyatauIntPropertykustom) untuk menghindari refleksi sama sekali. Jika akses properti reflektif tidak dapat dihindari, anotasikan pengambil atau penyetel tertentu dengan@UsedByReflection.
Pengoptimalan atau obfuscation menyeluruh menonaktifkan
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Alasan hal ini menurunkan performa: Menempatkan
-dontoptimizeatau-dontshrinkdi dalam file.flagsakan mengganti setelanAndroid.bpmodul secara otomatis dan menonaktifkan penerusan pengoptimalan di seluruh target. Lebih buruk lagi, jika library mengekspor file.flagsyang berisi-dontoptimize, library tersebut akan menonaktifkan pengoptimalan R8 untuk setiapandroid_appdownstream yang menautkan library. - Perbaikan yang direkomendasikan: Hapus
-dontoptimize,-dontshrink, dan-dontobfuscatedari file.flags. Kontrol perilaku pengoptimalan secara eksplisit diAndroid.bpmenggunakan blokoptimize(shrink,optimize,obfuscate) pada target leaf.
Flag diagnostik dalam aturan yang di-commit
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Alasan hal ini mengganggu build: Flag diagnostik membanjiri log build atau mencoba menulis file output ke jalur lokal selama build Soong dengan sandbox.
- Perbaikan yang direkomendasikan: Hapus flag ini dari file
.flagsyang di-commit. Soong secara otomatis menulis output penggunaan dan pemetaan R8, sepertiproguard_dictionarydanproguard_usage.zip, ke direktori perantara modul di bagianout/soong/.intermediates/.
Aturan keep produksi untuk kode pengujian
- Alasan hal ini mengganggu performa: Menambahkan aturan
-keepkustom hanya untuk akses pengujian akan memaksa kode tersebut tetap tidak di-obfuscate dan dipertahankan dalam build produksi. Meskipun mengonfigurasitrace_references_from(atauallow_traced_referencesdengantrace_references: true) atau menganotasi metode dengan@VisibleForTestingmenghindari pemeliharaan aturan-keepmanual dan hanya mempertahankan apa yang direferensikan pengujian, simbol tersebut masih dikirim dalam biner produksi. - Perbaikan yang direkomendasikan: Hilangkan aturan
-keeppengujian kustom dengan mengaktifkantrace_references_from(atauallow_traced_referencesdantrace_references: true), atau susun aplikasi ke dalam library.implyang ditautkan ke APK pengujian yang melakukan instrumentasi sendiri menggunakanstatic_libsuntuk menjaga titik entri khusus pengujian sepenuhnya keluar dari APK produksi, seperti yang dijelaskan dalam Langkah 2: Migrasikan 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 hal ini menurunkan performa: Karakter pengganti menyeluruh mempertahankan setiap resource dalam
jenis yang cocok di seluruh modul dan dependensinya, sehingga menggagalkan penyingkatan
resource (
shrink_resources: true) serta memperbesar APK dan tabelresources.arscyang dipetakan. - Perbaikan yang direkomendasikan:
- Lebih pilih referensi resource statis daripada
keep.xml: Hindari penggunaan metodeResources.getIdentifieruntuk mencari kumpulan resource yang dibatasi secara dinamis, seperti string eksperimen bernomor atau drawable bertema. Sebagai gantinya, gunakan pernyataanswitchwaktu kompilasi atau petakan konstantaR.id,R.string, atauR.drawablestatis. Referensi statis memungkinkan R8 dan AAPT2 melacak resource aktif yang tepat, menghilangkan overhead pencarian string runtime, dan sepenuhnya menghilangkan kebutuhan akankeep.xml. - Cantumkan nama resource tertentu: Jika pencarian dinamis diperlukan, misalnya
oleh pembaca lisensi pihak ketiga, cantumkan ID resource yang tepat di
tools:keep(misalnya,tools:keep="@raw/third_party_licenses"). - Hapus file
keep.xmlyang redundan: Jika resource yang tercantum sudah direferensikan secara statis dalam kode (R.raw.foo) atau XML (@raw/foo), penyingkat akan mempertahankannya secara otomatis, sehingga Anda dapat menghapusres/raw/keep.xml.
- Lebih pilih referensi resource statis daripada
Memverifikasi dan mengaudit perubahan aturan
Setiap kali Anda menghapus atau mempersempit aturan keep di proguard.flags atau keep.xml,
pastikan modul di-build dengan baik, lulus uji unit dan instrumentasi,
serta mempertahankan semua titik entri yang diperlukan.
Modul build dan pengujian
Bangun modul dengan bersih untuk memverifikasi bahwa R8 dan AAPT2 selesai tanpa peringatan referensi yang hilang:
m <MODULE_NAME>Jalankan pengujian unit dan instrumentasi untuk modul menggunakan
atest:atest <TEST_MODULE_NAME>Untuk aplikasi sistem, layanan dengan hak istimewa, atau modul yang bergantung pada hardware, jalankan pengujian instrumentasi dan UI pada perangkat fisik target atau di lab pengujian perangkat Anda untuk menjalankan 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
apkanalyzerataudexdumpuntuk memeriksa class, metode, dan kolom yang dipertahankan dalam file APK atau DEX output:apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkSaat mengubah
keep.xmlatau mengaktifkanshrink_resources, gunakanaapt2 dump resourcesuntuk memverifikasi bahwa build menghapus resource yang tidak digunakan dan mempertahankan resource yang diperlukan:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkSaat menjalankan
prebuilts/r8/r8.jarlangsung padaclasses.jarpra-R8 modul untuk memverifikasi perubahan flag secara offline tanpa build platform penuh, teruskan JAR classpath--libmodul (android.jar,framework.jar, danlibsbersama) dan aturanproguard.optionsyang dihasilkan AAPT2 dariAndroidManifest.xmldan XML tata letak. Tanpa input tersebut, R8 tidak dapat me-resolve superjenis framework (Activity,Service,Phone,Handler) dan kurang mempertahankan metode hierarki.
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
pasti dari setiap aturan keep selama kompilasi. Soong mengintegrasikan penganalisis ini
langsung ke build platform Android (dikonfigurasi di
build/soong/java/dex.go).
Untuk menganalisis aturan keep untuk modul platform atau di seluruh build:
Jalankan build dengan variabel lingkungan
R8_DUMP_KEEP_RADIUS=true:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Jika
R8_DUMP_KEEP_RADIUS=trueditetapkan, Soong akan menginstruksikan R8 untuk mencatat metrik aturan keep dalam filer8keepradius.pbperantara untuk setiap modul yang dikompilasi di bagianout/soong/.intermediates/. Untuk setiap aturan keep dan anotasikeepanno, R8 mencatat:- Radius keep langsung: Class, kolom, dan metode yang tepat yang dipertahankan oleh aturan, beserta batasan spesifik yang diterapkan terhadap penyingkatan, pengoptimalan, atau obfuscation.
- Subsumsi aturan: Aturan atau anotasi keep lain yang sudah mempertahankan item yang sama. Jika aturan kustom sepenuhnya tercakup dalam aturan AAPT2 atau aturan dasar pengukuran global, Anda dapat menghapusnya dengan aman.
- Aturan global dan seluruh paket: Aturan yang menggunakan karakter pengganti seluruh paket yang luas atau menerapkan perintah konfigurasi global.
Konversikan output
r8keepradius.pbmenjadi laporan HTML interaktif menggunakanKeepRadiusHtmlReportGenerator(dipaketkan dalamprebuilts/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_reportsSaat diberi direktori,
KeepRadiusHtmlReportGeneratorakan menelusuri semua file*keepradius*.pb, membuat laporan HTML untuk setiap modul, dan membuatout/keep_radius_reports/keepradius.htmlyang meringkas jumlah item aktif, jumlah item yang dipertahankan, dan aturan keep radius tertinggi di seluruh build. Untuk mengetahui informasi selengkapnya tentang cara menafsirkan skor penyingkatan, pengoptimalan, dan obfuscation dalam laporan yang dihasilkan, lihat Menggunakan R8 Configuration Analyzer.
Soong juga mendukung variabel lingkungan loop dalam dan diagnostik R8 tambahan
di build/soong/java/dex.go:
R8_DUMP_INPUT=true: Menulisr8inputs.zipke direktori perantara modul yang berisi semua JAR input, JAR library, dan konfigurasi ProGuard gabungan untuk reproduksi R8 mandiri.R8_DUMP_PERFETTO_TRACE=true: Menulisr8trace.ptraceke direktori perantara modul untuk memeriksa penerusan kompilasi R8 di Perfetto.SOONG_SKIP_TRACE_REFERENCES=true: Melewati eksekusi aturantracereferencesyang dikonfigurasi olehtrace_references_fromatautrace_referencesuntuk mempercepat build loop dalam lokal saat melakukan iterasi pada kode pengujian.
Memvalidasi aturan konsumen library
Saat java_library atau android_library mengekspor aturan keep ke aplikasi pelanggan downstream (export_proguard_flags_files: true), aturan tersebut tidak boleh menyertakan flag global yang mengubah atau menonaktifkan pengoptimalan untuk aplikasi pelanggan.
R8 menyediakan penganalisis aturan keep open source
(ProcessKeepRules), yang diekspos dalam build platform Android
sebagai alat host process-keep-rules (ditentukan 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 perintah yang tidak diizinkan dalam aturan
konsumen library. Perintah yang tidak diizinkan mencakup pengoptimalan global, penyingkatan,
atau penonaktifan obfuscation (seperti -dontoptimize atau -dontshrink), pengemasan ulang
paket dan flag modifikasi akses, flag diagnostik atau pemetaan, dan
-keepattributes level aplikasi.
Mengaudit hierarki sumber dengan pgaudit.py
Untuk mengaudit direktori sumber yang belum di-build bersama 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 pengukuran platform global, karakter pengganti yang luas, penggantian
-dontoptimize, dan aturan keep 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 hierarki 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.