Untuk membuat aplikasi Anda sekecil dan secepat mungkin, Anda harus mengoptimalkan dan meminifikasi
build rilis Anda dengan isMinifyEnabled = true
.
Tindakan ini akan mengaktifkan penyingkatan, yang akan menghapus kode dan resource yang tidak digunakan; obfuscation, yang mempersingkat nama class dan anggota aplikasi; dan pengoptimalan, yang menerapkan strategi yang lebih agresif untuk lebih mengurangi ukuran dan meningkatkan performa aplikasi Anda. Halaman ini menjelaskan bagaimana R8 melakukan tugas-tugas waktu kompilasi ini untuk proyek Anda dan bagaimana Anda dapat menyesuaikan mereka.
Saat Anda membuat project menggunakan plugin Android Gradle 3.4.0 atau yang lebih baru, plugin ini tidak lagi menggunakan ProGuard untuk melakukan pengoptimalan kode waktu kompilasi. Sebagai gantinya, plugin ini berfungsi dengan compiler R8 untuk menangani tugas-tugas waktu kompilasi berikut:
- Penyingkatan kode (atau tree-shaking): mendeteksi dan menghapus dengan aman class, kolom, metode, dan atribut yang tidak digunakan dari aplikasi Anda dan dependensi library-nya (menjadikannya alat yang berharga untuk mengatasi batasan referensi 64k). Misalnya, jika Anda hanya menggunakan beberapa API dari dependensi library, penyingkatan dapat mengidentifikasi kode library yang tidak digunakan aplikasi dan hanya menghapus kode tersebut dari aplikasi Anda. Untuk mempelajari lebih lanjut, buka bagian tentang cara menyingkat kode.
- Penyingkatan resource: menghapus resource yang tidak digunakan dari aplikasi yang dipaketkan, termasuk resource yang tidak digunakan dalam dependensi library aplikasi Anda. Ini berfungsi bersamaan dengan penyingkatan kode sehingga setelah kode yang tidak digunakan dihapus, semua resource yang tidak lagi direferensikan juga dapat dihapus dengan aman. Untuk mempelajari lebih lanjut, baca bagian tentang cara menyingkat resource.
- Pengoptimalan: memeriksa dan menulis ulang kode Anda untuk meningkatkan runtime
dan mengurangi ukuran file DEX aplikasi Anda. Ini
meningkatkan kinerja {i>runtime<i} kode hingga 30%, meningkat secara drastis
waktu mulai dan waktu render frame. Misalnya, jika R8 mendeteksi bahwa cabang
else {}
untuk pernyataan if/else tertentu tidak pernah digunakan, R8 akan menghapus kode untuk cabangelse {}
. Untuk mempelajari lebih lanjut, buka bagian tentang pengoptimalan kode. - Obfuscation (atau minifikasi ID): mempersingkat nama class dan anggota, yang menghasilkan ukuran file DEX yang lebih kecil. Untuk mempelajari lebih lanjut, buka bagian tentang cara meng-obfuscate kode.
Saat membangun versi rilis aplikasi, R8 dapat dikonfigurasi untuk menjalankan tugas-tugas waktu kompilasi yang dijelaskan di atas untuk Anda. Anda juga dapat menonaktifkan tugas atau menyesuaikan perilaku R8 melalui file aturan ProGuard. Faktanya, R8 berfungsi dengan semua file aturan ProGuard yang ada, jadi mengupdate plugin Android Gradle agar menggunakan R8 tidak akan mengharuskan Anda untuk mengubah aturan yang ada.
Mengaktifkan penyingkatan, obfuscation, dan pengoptimalan
Saat Anda menggunakan Android Studio 3.4 atau plugin Android Gradle 3.4.0 dan yang lebih baru, R8 adalah compiler default yang mengubah bytecode Java project Anda menjadi format DEX yang berjalan pada platform Android. Namun, saat Anda membuat project baru menggunakan Android Studio, penyingkatan, obfuscation, dan pengoptimalan kode tidak diaktifkan secara default. Hal itu karena pengoptimalan waktu kompilasi ini meningkatkan waktu build project dan dapat memunculkan bug jika Anda tidak menyesuaikan kode yang perlu dipertahankan secara memadai.
Jadi, sebaiknya aktifkan tugas-tugas waktu kompilasi ini saat membuat versi final aplikasi yang Anda uji sebelum dipublikasikan. Untuk mengaktifkan penyingkatan, obfuscation, dan pengoptimalan, sertakan yang berikut ini dalam skrip build level project Anda.
Kotlin
android { buildTypes { getByName("release") { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `isDebuggable=false`. isMinifyEnabled = true // Enables resource shrinking, which is performed by the // Android Gradle plugin. isShrinkResources = true proguardFiles( // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. getDefaultProguardFile("proguard-android-optimize.txt"), // Includes a local, custom Proguard rules file "proguard-rules.pro" ) } } ... }
Groovy
android { buildTypes { release { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `debuggable false`. minifyEnabled true // Enables resource shrinking, which is performed by the // Android Gradle plugin. shrinkResources true // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. proguardFiles getDefaultProguardFile( 'proguard-android-optimize.txt'), 'proguard-rules.pro' } } ... }
File konfigurasi R8
R8 menggunakan file aturan ProGuard untuk mengubah perilaku defaultnya dan memahami struktur aplikasi Anda dengan lebih baik, seperti class yang berfungsi sebagai titik masuk ke dalam kode aplikasi Anda. Meskipun Anda dapat mengubah beberapa file aturan ini, beberapa aturan dapat otomatis dibuat oleh fitur waktu kompilasi, seperti AAPT2, atau diwarisi dari dependensi library aplikasi Anda. Tabel di bawah ini menjelaskan sumber file aturan ProGuard yang digunakan R8.
Sumber | Lokasi | Deskripsi |
Android Studio | <module-dir>/proguard-rules.pro
|
Saat Anda membuat modul baru menggunakan Android Studio, IDE akan membuat file proguard-rules.pro di direktori utama dari modul tersebut.
Secara default, file ini tidak menerapkan aturan apa pun. Jadi, sertakan aturan ProGuard Anda sendiri di sini, seperti aturan keep kustom. |
Plugin Android Gradle | Dihasilkan oleh plugin Android Gradle pada waktu kompilasi. | Plugin Android Gradle menghasilkan
proguard-android-optimize.txt , yang menyertakan aturan yang
berguna untuk sebagian besar project Android dan memungkinkan
anotasi
@Keep* .
Secara default, saat membuat modul baru menggunakan Android Studio, level modul akan skrip build menyertakan file aturan ini dalam build rilis Anda keamanan untuk Anda.
Catatan: Plugin Android Gradle menyertakan file aturan ProGuard tambahan
yang telah ditetapkan sebelumnya, tetapi sebaiknya Anda menggunakan
|
Dependensi library |
Di library AAR:
Di library JAR: Selain lokasi ini, plugin Android Gradle 3.6 atau yang lebih tinggi juga mendukung aturan penyusutan yang ditargetkan. |
Jika library AAR atau JAR dipublikasikan dengan file aturannya sendiri, dan Anda menyertakan library tersebut sebagai dependensi waktu kompilasi, R8 secara otomatis menerapkan aturan-aturan itu ketika mengompilasi proyek Anda. Selain aturan ProGuard konvensional, plugin Android Gradle 3.6 atau yang lebih tinggi juga mendukung aturan penyusutan yang ditargetkan. Ini adalah aturan yang menargetkan penyingkat khusus (R8 atau ProGuard), serta versi penyingkat. Menggunakan file aturan yang dikemas dengan pustaka akan berguna jika suatu aturan diperlukan agar library berfungsi dengan baik—yaitu, library pengembang telah melakukan langkah pemecahan masalah untuk Anda. Namun, Anda harus mengetahui bahwa karena aturan ini bersifat aditif, aturan tertentu yang disertakan oleh dependensi library tidak dapat dihapus dan mungkin memengaruhi kompilasi bagian lain aplikasi Anda. Misalnya, jika mencakup aturan untuk menonaktifkan pengoptimalan kode, yaitu aturan yang menonaktifkan pengoptimalan untuk seluruh proyek. |
Android Asset Package Tool 2 (AAPT2) | Setelah membuat project dengan minifyEnabled true :
<module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt
|
AAPT2 akan membuat aturan keep berdasarkan referensi ke class dalam manifes aplikasi, tata letak, dan resource aplikasi Anda lainnya. Misalnya, AAPT2 menyertakan aturan keep untuk setiap Aktivitas yang Anda daftarkan dalam manifes aplikasi Anda sebagai titik entri. |
File konfigurasi kustom | Secara default, saat membuat modul baru menggunakan Android Studio, IDE
akan membuat <module-dir>/proguard-rules.pro agar Anda dapat menambahkan
aturan Anda sendiri.
|
Anda dapat menyertakan konfigurasi tambahan, dan R8 akan menerapkannya pada waktu kompilasi. |
Saat Anda menetapkan properti minifyEnabled
ke true
, R8 akan menggabungkan aturan dari semua
sumber yang tersedia yang tercantum di atas. Hal ini penting diingat saat Anda
memecahkan masalah dengan R8, karena dependensi waktu kompilasi lainnya,
seperti dependensi library, dapat memunculkan perubahan pada perilaku R8 yang
tidak Anda ketahui.
Untuk meng-output laporan lengkap dari semua aturan yang diterapkan R8 saat membuat project
Anda, sertakan baris berikut dalam file proguard-rules.pro
modul Anda:
// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt
Aturan penyusutan yang ditargetkan
Plugin Android Gradle 3.6 atau yang lebih tinggi mendukung library aturan yang menargetkan penyingkat khusus (R8 atau ProGuard), serta versi penyingkat tertentu. Ini memungkinkan developer library menyesuaikan aturan mereka agar berfungsi secara optimal dalam project yang menggunakan versi penyingkat baru, sekaligus mengizinkan aturan yang sudah ada digunakan dalam proyek-proyek dengan versi penyingkat yang lebih lama.
Untuk menentukan aturan penyusutan yang ditargetkan, developer library harus menyertakannya di lokasi tertentu dalam library AAR atau JAR, seperti yang dijelaskan di bawah.
In an AAR library:
proguard.txt (legacy location)
classes.jar
└── META-INF
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
In a JAR library:
META-INF
├── proguard/<ProGuard-rules-file> (legacy location)
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
Artinya aturan penyusutan yang ditargetkan disimpan di META-INF/com.android.tools
direktori JAR atau di direktori META-INF/com.android.tools
di dalam
classes.jar
AAR.
Di bawah direktori itu, bisa ada beberapa
direktori dengan nama dalam formulir
dari r8-from-<X>-upto-<Y>
atau proguard-from-<X>-upto-<Y>
untuk menunjukkan
versi yang penyingkat aturan
di dalam direktori yang dibuat.
Perhatikan bahwa bagian -from-<X>
dan -upto-<Y>
bersifat opsional, sedangkan versi <Y>
bersifat eksklusif, dan rentang versinya harus berkelanjutan.
Misalnya, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
, dan r8-from-8.2.0
membentuk kumpulan aturan penyusutan yang ditargetkan. Aturan yang tercakup dalam
Direktori r8-from-8.0.0-upto-8.2.0
akan digunakan oleh R8 dari versi 8.0.0 hingga
tetapi tidak termasuk versi 8.2.0.
Dengan informasi tersebut, plugin Android Gradle 3.6 atau yang lebih tinggi akan memilih
aturan dari direktori R8 yang cocok. Jika library tidak menentukan target
aturan penyusutan, plugin Android Gradle akan memilih aturan dari
lokasi (proguard.txt
untuk AAR atau
META-INF/proguard/<ProGuard-rules-file>
untuk JAR).
Developer library dapat memilih untuk menyertakan aturan penyusutan yang ditargetkan atau aturan lama Aturan ProGuard dalam library mereka, atau kedua jenis tersebut jika ingin dipertahankan kompatibilitas dengan plugin Android Gradle yang lebih lama dari 3.6 atau fitur lainnya.
Menyertakan konfigurasi tambahan
Saat Anda membuat project atau modul baru menggunakan Android Studio, IDE akan membuat
file <module-dir>/proguard-rules.pro
agar Anda dapat menyertakan aturan Anda sendiri. Anda
dapat juga menyertakan aturan tambahan dari file lain dengan menambahkannya ke
proguardFiles
di skrip build modul Anda.
Misalnya, Anda dapat menambahkan aturan yang berlaku khusus untuk setiap varian build dengan menambahkan
properti proguardFiles
lain di blok productFlavor
yang terkait. File
Gradle berikut menambahkan flavor2-rules.pro
ke ragam produk flavor2
.
Sekarang, flavor2
menggunakan ketiga aturan ProGuard karena aturan dari blok release
juga diterapkan.
Selain itu, Anda dapat menambahkan properti testProguardFiles
, yang menentukan
daftar file ProGuard yang disertakan dalam APK pengujian saja:
Kotlin
android { ... buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). "proguard-rules.pro" ) testProguardFiles( // The proguard files listed here are included in the // test APK only. "test-proguard-rules.pro" ) } } flavorDimensions.add("version") productFlavors { create("flavor1") { ... } create("flavor2") { proguardFile("flavor2-rules.pro") } } }
Groovy
android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). 'proguard-rules.pro' testProguardFiles // The proguard files listed here are included in the // test APK only. 'test-proguard-rules.pro' } } flavorDimensions "version" productFlavors { flavor1 { ... } flavor2 { proguardFile 'flavor2-rules.pro' } } }
Menyingkat kode
Penyingkatan kode dengan R8 diaktifkan secara default saat Anda menetapkan properti minifyEnabled
ke true
.
Penyingkatan kode (disebut juga tree shaking) adalah proses menghapus kode yang menurut R8 tidak diperlukan selama runtime. Proses ini dapat mengurangi ukuran aplikasi Anda secara signifikan jika, misalnya, aplikasi Anda menyertakan banyak dependensi library tetapi hanya menggunakan sebagian kecil fungsinya.
Untuk menyingkat kode aplikasi Anda, pertama-tama R8 akan menentukan semua titik entri ke kode aplikasi Anda berdasarkan set file konfigurasi gabungan. Titik entri ini menyertakan semua class yang dapat digunakan platform Android untuk membuka Aktivitas atau layanan aplikasi Anda. Mulai dari setiap titik entri, R8 akan memeriksa kode aplikasi Anda untuk membuat grafik dari semua metode, variabel anggota, dan class lain yang mungkin diakses aplikasi Anda saat runtime. Kode yang tidak terhubung ke grafik tersebut dianggap tidak dapat diakses dan dapat dihapus dari aplikasi.
Gambar 1 menunjukkan aplikasi dengan dependensi library runtime. Selagi memeriksa kode aplikasi, R8 menyimpulkan bahwa metode foo()
, faz()
, dan bar()
dapat diakses dari titik entri MainActivity.class
. Namun, class OkayApi.class
atau metode baz()
-nya tidak pernah digunakan oleh aplikasi Anda pada runtime, dan R8 akan menghapus kode itu saat menyusutkan aplikasi Anda.
Gambar 1. Pada waktu kompilasi, R8 membuat grafik berdasarkan gabungan aturan keep project Anda untuk menentukan kode yang tidak dapat diakses.
R8 menentukan titik entri melalui aturan -keep
dalam
file konfigurasi R8 project. Artinya, aturan keep menentukan class yang tidak boleh dihapus oleh R8 saat menyusutkan aplikasi Anda, dan R8 menganggap class tersebut sebagai titik entri ke dalam aplikasi Anda. Plugin Android Gradle dan AAPT2 otomatis menghasilkan aturan keep yang diperlukan oleh sebagian besar project aplikasi, seperti aktivitas, tampilan, dan layanan aplikasi Anda. Namun,
jika Anda perlu menyesuaikan perilaku default ini dengan aturan keep tambahan, baca
bagian tentang cara menyesuaikan kode yang perlu dipertahankan.
Jika Anda hanya tertarik untuk mengurangi ukuran resource aplikasi, langsung baca cara menyingkat resource.
Perhatikan bahwa jika project library menyusut, aplikasi yang bergantung pada library tersebut mencakup class library yang menyusut. Anda mungkin perlu menyesuaikan aturan {i>library<i} jika ada class yang hilang dalam APK library. Jika Anda membangun dan memublikasikan library dalam format AAR, file JAR lokal yang tidak menjadi dependensi library Anda dikecilkan dalam file AAR.
Menyesuaikan kode yang perlu dipertahankan
Untuk sebagian besar situasi, file aturan ProGuard default (proguard-android-optimize.txt
)
cukup bagi R8 untuk
menghapus kode yang tidak digunakan saja. Namun,
beberapa situasi sulit dianalisis dengan benar oleh R8 dan akibatnya R8 mungkin menghapus
kode yang sebenarnya diperlukan aplikasi Anda. Beberapa contoh di mana R8 mungkin salah menghapus
kode antara lain:
- Jika aplikasi Anda memanggil metode dari Java Native Interface (JNI)
- Jika aplikasi Anda mencari kode saat runtime (seperti dengan refleksi)
Pengujian aplikasi akan menunjukkan error yang disebabkan oleh kode yang salah dihapus, tetapi Anda juga dapat memeriksa kode apa yang telah dihapus dengan membuat laporan kode yang dihapus.
Untuk memperbaiki error dan memaksa R8 agar mempertahankan kode tertentu, tambahkan
baris -keep
dalam file aturan ProGuard. Contoh:
-keep public class MyClass
Atau, Anda dapat menambahkan
anotasi @Keep
ke kode
yang ingin dipertahankan. Menambahkan @Keep
pada suatu class akan mempertahankan seluruh class tersebut apa adanya.
Menambahkannya pada suatu metode atau kolom akan mempertahankan metode/kolom tersebut (dan namanya) serta
nama class-nya tetap utuh. Perhatikan bahwa anotasi ini hanya tersedia saat
menggunakan
AndroidX Annotations Library
dan saat Anda menyertakan file aturan ProGuard yang dikemas dengan plugin Android
Gradle, seperti dijelaskan di bagian tentang cara
mengaktifkan penyingkatan.
Ada banyak pertimbangan yang harus Anda buat saat menggunakan opsi -keep
; untuk
informasi selengkapnya tentang menyesuaikan file aturan, baca
Panduan ProGuard.
Bagian
Pemecahan Masalah
dalam panduan tersebut menjelaskan masalah umum lain yang mungkin Anda alami jika
kode dihapus.
Menghapus library native
Secara default, library kode native dihilangkan di build rilis aplikasi Anda. Pembersihan ini terdiri dari penghapusan tabel simbol dan informasi proses debug yang dimuat dalam library native yang digunakan oleh aplikasi Anda. Pembersihan library kode native menghasilkan penghematan ukuran yang signifikan; tetapi, Anda tidak dapat mendiagnosis error pada Konsol Google Play karena ada informasi yang tidak ada (seperti nama class dan fungsi).
Dukungan untuk masalah pada native code
Konsol Google Play melaporkan masalah pada native code di Android vitals. Dengan beberapa langkah, Anda dapat membuat dan mengupload file simbol debug native untuk aplikasi Anda. File ini memungkinkan pelacakan tumpukan error native tersimbolkan (yang mencakup nama fungsi dan class) di Android vitals untuk membantu Anda men-debug aplikasi dalam produksi. Langkah-langkah ini bervariasi bergantung pada versi plugin Android Gradle yang digunakan dalam project Anda dan output build project Anda.
Versi Plugin Android Gradle: 4.1 atau yang lebih baru
Jika project mem-build Android App Bundle, Anda dapat otomatis menyertakan
file simbol debug native di dalamnya. Untuk menyertakan file ini dalam build rilis, tambahkan
hal berikut ke file build.gradle.kts
aplikasi Anda:
android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }
Pilih level simbol debug berikut ini:
- Gunakan
SYMBOL_TABLE
untuk mendapatkan nama fungsi di pelacakan tumpukan yang disimbolkan Konsol Play. Level ini mendukung tombstone. - Gunakan
FULL
untuk mendapatkan nama fungsi, file, dan nomor baris dalam pelacakan tumpukan yang disimbolkan Konsol Play.
Jika project Anda mem-build APK, gunakan setelan build build.gradle.kts
yang ditampilkan
di awal untuk membuat file simbol debug native secara terpisah. Mengupload file simbol debug native
secara manual
ke Konsol Google Play. Sebagai bagian dari proses build, plugin
Android Gradle akan menampilkan file ini pada lokasi project berikut:
app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip
Plugin Android Gradle versi 4.0 atau yang lebih lama (dan sistem build lainnya)
Sebagai bagian dari proses build, plugin Android Gradle menyimpan salinan library simbolik dalam direktori project. Struktur direktori ini mirip dengan yang berikut:
app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── arm64-v8a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── x86/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
└── x86_64/
├── libgameengine.so
├── libothercode.so
└── libvideocodec.so
Gunakan konten direktori ini:
cd app/build/intermediates/cmake/universal/release/obj
zip -r symbols.zip .
Upload file
symbols.zip
secara manual ke Konsol Google Play.
Menyingkat resource
Penyingkatan resource hanya dapat dilakukan bersama dengan penyingkatan kode. Setelah penyingkat kode menghapus semua kode yang tidak terpakai, penyingkat resource dapat mengidentifikasi resource mana saja yang masih digunakan aplikasi. Hal ini terjadi terutama saat Anda menambahkan library kode yang menyertakan resource—Anda harus menghapus kode library yang tidak digunakan sehingga resource library tersebut menjadi tidak dirujuk dan, dengan demikian, dapat dihapus oleh penyingkat resource.
Untuk mengaktifkan penyingkatan resource, tetapkan properti shrinkResources
ke true
di skrip build Anda (bersama
minifyEnabled
untuk penyingkatan kode). Contoh:
Kotlin
android { ... buildTypes { getByName("release") { isShrinkResources = true isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } }
Groovy
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
Jika Anda belum membuat aplikasi menggunakan minifyEnabled
untuk
penyingkatan kode, cobalah itu sebelum mengaktifkan shrinkResources
,
karena Anda mungkin perlu mengedit file proguard-rules.pro
untuk
mempertahankan class atau metode yang dibuat atau dipanggil secara dinamis sebelum Anda
mulai menghapus resource.
Menyesuaikan resource yang perlu dipertahankan
Jika ada resource tertentu yang ingin dipertahankan atau dihapus, buatlah file XML
dalam project dengan tag <resources>
, lalu tentukan setiap
resource yang ingin dipertahankan dalam atribut tools:keep
dan setiap resource
yang ingin dihapus dalam atribut tools:discard
. Kedua atribut ini menerima
daftar nama resource yang dipisahkan koma. Anda dapat menggunakan karakter tanda bintang sebagai
karakter pengganti.
Contoh:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
Simpan file ini dalam resource project, misalnya, di
res/raw/my.package.keep.xml
. Build tidak memaketkan file ini ke dalam
aplikasi Anda.
Catatan: Pastikan Anda menggunakan nama yang unik untuk file keep
. Kapan
{i>library<i} yang berbeda saling terhubung,
aturan keep mereka akan bertentangan
sebaliknya, menyebabkan potensi masalah dengan aturan yang diabaikan atau aturan yang tidak diperlukan
Google Cloud Platform.
Menentukan resource yang akan dihapus mungkin tampak aneh karena sebenarnya Anda bisa
langsung menghapusnya, tetapi cara ini berguna saat Anda menggunakan varian build. Sebagai
misalnya, Anda dapat menempatkan semua resource
ke dalam direktori proyek umum,
lalu buat file my.package.build.variant.keep.xml
yang berbeda untuk masing-masing
ketika Anda mengetahui bahwa sumber daya tertentu tampaknya digunakan dalam kode
(dan karenanya tidak dihapus oleh penyingkat) tetapi Anda tahu bahwa sebenarnya tidak
yang digunakan untuk varian build yang ditentukan. Mungkin juga alat build
salah mengidentifikasi sumber daya sebagai
yang dibutuhkan, yang mungkin terjadi karena
compiler menambahkan ID resource secara inline dan penganalisis resource mungkin tidak
ketahui perbedaan antara sumber daya yang benar-benar direferensikan dan nilai bilangan bulat
dalam kode yang kebetulan
memiliki nilai yang sama.
Mengaktifkan pemeriksaan referensi yang ketat
Biasanya, penyingkat resource dapat mengetahui secara akurat saat sebuah resource
digunakan. Akan tetapi, jika kode Anda membuat panggilan ke
Resources.getIdentifier()
(atau jika salah satu library Anda melakukannya—library AppCompat
biasanya melakukan panggilan ini), hal itu berarti kode Anda menelusuri nama resource berdasarkan
string yang dihasilkan secara dinamis. Saat Anda melakukan ini, secara default penyingkat resource akan berperilaku
defensif dan menandai semua resource dengan format nama yang cocok sebagai
berpotensi digunakan dan tidak boleh dihapus.
Misalnya, kode berikut menyebabkan semua resource dengan
awalan img_
ditandai sebagai digunakan.
Kotlin
val name = String.format("img_%1d", angle + 1) val res = resources.getIdentifier(name, "drawable", packageName)
Java
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
Penyingkat resource juga memeriksa semua konstanta string dalam kode
Anda, serta berbagai resource res/raw/
, untuk menemukan URL
resource dalam format yang mirip dengan
file:///android_res/drawable//ic_plus_anim_016.png
. Jika menemukan string seperti ini atau string lain yang tampaknya dapat digunakan untuk membuat URL seperti ini, maka penyingkat tidak akan menghapusnya.
Berikut ini contoh dari mode penyingkatan aman yang diaktifkan secara default.
Namun, Anda dapat menonaktifkan penanganan "lebih baik mencegah daripada mengobati" ini, dan menentukan bahwa penyingkat resource hanya mempertahankan resource yang benar-benar digunakan. Caranya,
tetapkan shrinkMode
ke strict
dalam
file keep.xml
, seperti berikut:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:shrinkMode="strict" />
Jika mengaktifkan mode penyingkatan ketat dan kode Anda juga merujuk resource dengan string yang dihasilkan secara dinamis, seperti yang ditampilkan di atas, maka Anda harus secara manual mempertahankan resource menggunakan atribut tools:keep
.
Menghapus resource alternatif yang tidak digunakan
Penyingkat resource Gradle hanya akan menghapus resource yang tidak direferensikan
oleh kode aplikasi Anda, yang berarti penyingkat tidak akan menghapus
resource alternatif untuk konfigurasi perangkat yang berbeda. Jika perlu,
Anda dapat menggunakan properti resConfigs
plugin Android Gradle untuk
menghapus file resource alternatif yang tidak diperlukan aplikasi.
Misalnya, jika Anda menggunakan library yang menyertakan resource bahasa (seperti AppCompat atau Layanan Google Play), maka aplikasi Anda akan menyertakan semua string bahasa yang diterjemahkan untuk pesan di library tersebut, tanpa melihat apakah bagian lain aplikasi Anda diterjemahkan ke bahasa yang sama atau tidak. Jika hanya ingin
mempertahankan bahasa yang resmi didukung aplikasi, Anda dapat menentukannya
menggunakan properti resConfig
. Setiap resource
bahasa yang tidak ditentukan akan dihapus.
Cuplikan berikut menampilkan cara membatasi resource bahasa hanya ke bahasa Inggris dan Prancis:
Kotlin
android { defaultConfig { ... resourceConfigurations.addAll(listOf("en", "fr")) } }
Groovy
android { defaultConfig { ... resConfigs "en", "fr" } }
Saat merilis aplikasi menggunakan format Android App Bundle, secara default hanya bahasa yang dikonfigurasi pada perangkat pengguna yang akan didownload saat menginstal aplikasi. Demikian pula, hanya resource yang cocok dengan kepadatan layar perangkat, dan library native yang cocok dengan ABI perangkat, yang disertakan dalam download. Untuk mengetahui informasi selengkapnya, lihat Konfigurasi Android App Bundle.
Untuk aplikasi lama yang dirilis dengan APK (dibuat sebelum Agustus 2021), Anda dapat menyesuaikan kepadatan layar atau resource ABI yang akan disertakan dalam APK dengan mem-build beberapa APK yang masing-masing menargetkan konfigurasi perangkat yang berbeda.
Menggabungkan resource duplikat
Secara default, Gradle juga menggabungkan resource bernama identik, seperti
drawable dengan nama sama yang mungkin berada dalam folder resource berbeda. Perilaku
ini tidak dikendalikan oleh properti shrinkResources
dan
tidak dapat dinonaktifkan, karena diperlukan untuk mencegah error saat terdapat beberapa
resource yang namanya sama dengan nama kode yang Anda cari.
Penggabungan resource hanya terjadi saat dua atau beberapa file menggunakan nama, jenis, dan penentu resource yang sama. Gradle memilih file mana yang dianggap sebagai pilihan terbaik di antara duplikat tersebut (berdasarkan urutan prioritas yang dijelaskan di bawah) dan meneruskan hanya satu resource ke AAPT untuk didistribusikan dalam artefak final.
Gradle mencari resource duplikat di lokasi berikut:
- Resource utama, yang terkait dengan set sumber utama, biasanya terletak di
src/main/res/
. - Overlay varian, dari jenis build dan ragam build.
- Dependensi project library.
Gradle menggabungkan resource duplikat dalam urutan prioritas berikut:
Dependensi → Utama → Ragam build → Jenis build
Misalnya, jika sebuah resource duplikat muncul di resource utama dan ragam build, maka Gradle akan memilih resource yang berada di ragam build.
Jika resource identik muncul dalam set sumber yang sama, Gradle tidak dapat menggabungkannya dan akan mengeluarkan error penggabungan resource. Hal ini dapat terjadi jika Anda menentukan beberapa set sumber di properti sourceSet
dari file build.gradle.kts
—misalnya jika src/main/res/
dan src/main/res2/
berisi resource yang sama.
Meng-obfuscate kode
Tujuan obfuscation adalah mengurangi ukuran aplikasi dengan mempersingkat nama class, metode, dan kolom aplikasi Anda. Berikut ini adalah contoh obfuscation menggunakan R8:
androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
android.content.Context mContext -> a
int mListItemLayout -> O
int mViewSpacingRight -> l
android.widget.Button mButtonNeutral -> w
int mMultiChoiceItemLayout -> M
boolean mShowTitle -> P
int mViewSpacingLeft -> j
int mButtonPanelSideLayout -> K
Meskipun obfuscation tidak menghapus kode dari aplikasi Anda, penghematan ukuran yang signifikan dapat dicapai dalam aplikasi dengan file DEX yang mengindeks banyak class, metode, dan kolom. Namun, karena obfuscation mengubah nama berbagai bagian kode, tugas tertentu, seperti inspeksi pelacakan tumpukan, memerlukan fitur tambahan. Untuk memahami pelacakan tumpukan Anda setelah obfuscation, bacalah bagian tentang cara mendekode pelacakan tumpukan yang di-obfuscate.
Selain itu, jika kode Anda mengandalkan penamaan yang mudah diprediksi untuk metode dan class aplikasi Anda—saat menggunakan refleksi, misalnya, Anda harus memperlakukan tanda tangan tersebut sebagai titik entri dan menetapkan aturan keep untuknya, seperti dijelaskan di bagian cara menyesuaikan kode yang perlu dipertahankan. Aturan keep tersebut memberi tahu R8 untuk tidak hanya mempertahankan kode itu dalam DEX akhir aplikasi Anda, tetapi juga mempertahankan penamaan aslinya.
Mendekode pelacakan tumpukan yang di-obfuscate
Setelah R8 meng-obfuscate kode Anda, pelacakan tumpukan akan sulit (jika tidak mustahil) dipahami karena nama-nama class dan metode mungkin telah berubah. Untuk mendapatkan pelacakan tumpukan asli, Anda harus melakukan retrace pelacakan tumpukan.
Pengoptimalan kode
Untuk mengoptimalkan aplikasi Anda lebih jauh lagi, R8 akan memeriksa kode Anda secara lebih mendalam untuk menghapus lebih banyak kode yang tidak terpakai atau, jika memungkinkan, menulis ulang kode Anda untuk jadi lebih singkat. Berikut ini beberapa contoh pengoptimalan tersebut:
- Jika kode Anda tidak pernah mengambil cabang
else {}
untuk pernyataan if/else tertentu, R8 mungkin akan menghapus kode untuk cabangelse {}
. - Jika kode Anda memanggil metode hanya di beberapa tempat, R8 mungkin menghapus metode tersebut dan memasukkannya di beberapa situs panggilan.
- Jika R8 menentukan bahwa sebuah class hanya memiliki satu subclass unik, dan class itu sendiri tidak dipakai (misalnya, class dasar abstrak hanya digunakan oleh satu class implementasi konkret), maka R8 dapat menggabungkan dua class dan menghapus class dari aplikasi.
- Untuk mempelajari lebih lanjut, baca postingan blog pengoptimalan R8 oleh Jake Wharton.
R8 tidak memungkinkan Anda menonaktifkan atau mengaktifkan pengoptimalan terpisah, atau mengubah
perilaku pengoptimalan. Bahkan, R8 mengabaikan setiap aturan ProGuard yang mencoba
untuk mengubah pengoptimalan default, seperti -optimizations
dan
-optimizationpasses
. Pembatasan ini penting karena, seiring berkembangnya R8,
mempertahankan perilaku standar untuk pengoptimalan akan membantu tim Android
Studio memecahkan masalah dan menyelesaikan masalah apa pun yang mungkin Anda temui dengan mudah.
Perhatikan bahwa mengaktifkan pengoptimalan akan mengubah pelacakan tumpukan untuk aplikasi. Misalnya, inline akan menghapus frame stack. Lihat bagian tentang retrace untuk mempelajari cara mendapatkan pelacakan tumpukan asli.
Dampak pada performa runtime
Jika penyingkatan, obfuscation, dan pengoptimalan diaktifkan, R8 akan meningkatkan performa runtime kode (termasuk waktu startup dan frame di UI thread) hingga 30%. Menonaktifkan salah satunya secara drastis membatasi serangkaian pengoptimalan yang digunakan R8.
Jika R8 diaktifkan, Anda juga harus membuat Profil Startup untuk performa startup yang lebih baik lagi.
Mengaktifkan pengoptimalan yang lebih agresif
R8 mencakup serangkaian pengoptimalan tambahan (disebut sebagai "mode penuh") yang membuatnya berperilaku berbeda dari ProGuard. Pengoptimalan ini diaktifkan oleh default sejak Plugin Android Gradle versi 8.0.0.
Anda dapat menonaktifkan pengoptimalan tambahan ini dengan menyertakan
file gradle.properties
project Anda:
android.enableR8.fullMode=false
Karena pengoptimalan tambahan membuat R8 berperilaku berbeda dari ProGuard, mereka mungkin mengharuskan Anda menyertakan aturan ProGuard tambahan untuk menghindari runtime jika Anda menggunakan aturan yang dirancang untuk ProGuard. Misalnya, kode mereferensikan class melalui Java Reflection API. Jika tidak menggunakan "mode penuh," R8 berasumsi bahwa Anda bermaksud untuk memeriksa dan memanipulasi class tersebut saat runtime—meskipun kode Anda sebenarnya tidak—dan secara otomatis mempertahankan class dan penginisialisasi statisnya.
Namun, ketika menggunakan "mode penuh", R8 tidak membuat asumsi ini dan, jika R8 menegaskan bahwa jika tidak, kode Anda tidak akan pernah menggunakan class pada runtime, hal ini akan menghapus dari DEX akhir aplikasi Anda. Artinya, jika Anda ingin mempertahankan class dan penginisialisasi statis, Anda harus menyertakan aturan pertahanan dalam file aturan agar itu.
Jika Anda mengalami masalah saat menggunakan "mode penuh" R8, lihat Halaman FAQ R8 solusi yang memungkinkan. Jika Anda tidak dapat menyelesaikan masalah ini, harap laporkan bug.
Retrace pelacakan tumpukan
Kode yang diproses oleh R8 diubah dengan berbagai cara yang dapat membuat pelacakan tumpukan lebih sulit dipahami karena pelacakan tumpukan tidak akan sama persis dengan kode sumber. Hal ini dapat terjadi pada perubahan nomor baris saat informasi proses debug tidak disimpan. Hal ini dapat disebabkan oleh pengoptimalan seperti inline dan outline. Kontributor terbesar adalah obfuscation yang bahkan nama class dan metodenya akan berubah.
Untuk memulihkan pelacakan tumpukan yang asli, R8 menyediakan alat command line retrace, yang dipaketkan dengan paket alat command line.
Untuk mendukung retrace pelacakan tumpukan aplikasi, Anda harus memastikan
bahwa build menyimpan informasi yang cukup untuk melakukan retrace dengan menambahkan aturan
berikut ke file proguard-rules.pro
modul Anda:
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
Atribut LineNumberTable
menyimpan informasi posisi
dalam metode sehingga posisi tersebut dicetak dalam pelacakan tumpukan. Atribut SourceFile
memastikan semua runtime potensial benar-benar mencetak info posisi.
Perintah -renamesourcefileattribute
menetapkan nama file sumber dalam pelacakan
tumpukan hanya ke SourceFile
. Nama file sumber asli yang sebenarnya tidak diperlukan saat
melakukan retrace karena file pemetaan berisi file sumber asli.
R8 membuat file mapping.txt
setiap kali dijalankan, yang
berisi informasi yang diperlukan untuk memetakan pelacakan tumpukan kembali ke pelacakan tumpukan
awal. Android Studio menyimpan file ini dalam
direktori
<module-name>/build/outputs/mapping/<build-type>/
.
Saat memublikasikan aplikasi di Google Play, Anda dapat mengupload file mapping.txt
untuk setiap versi aplikasi Anda. Saat memublikasikan menggunakan Android App Bundle, file
ini disertakan secara otomatis sebagai bagian dari konten app bundle. Selanjutnya, Google Play akan melakukan retrace
pelacakan tumpukan yang masuk dari masalah yang dilaporkan pengguna, sehingga Anda dapat meninjaunya di
Konsol Play. Untuk mengetahui informasi selengkapnya, lihat artikel Pusat Bantuan
tentang cara
men-deobfuscate pelacakan tumpukan error.
Memecahkan masalah dengan R8
Bagian ini menjelaskan beberapa strategi untuk mengatasi masalah saat mengaktifkan penyingkatan, obfuscation, dan pengoptimalan menggunakan R8. Jika tidak dapat menemukan solusi untuk masalah Anda di bawah ini, baca juga halaman FAQ R8 dan panduan pemecahan masalah ProGuard.
Membuat laporan kode yang dihapus (atau dipertahankan)
Untuk membantu Anda memecahkan masalah R8 tertentu, sebaiknya lihat laporan
semua kode yang dihapus R8 dari aplikasi Anda. Untuk setiap modul yang ingin
Anda peroleh laporannya, tambahkan -printusage <output-dir>/usage.txt
ke
file aturan kustom Anda. Saat Anda mengaktifkan R8 dan membuat aplikasi, R8 akan meng-output
laporan yang berisi jalur dan nama file yang Anda tentukan. Laporan kode yang dihapus
terlihat mirip dengan berikut ini:
androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
public boolean hasWindowFeature(int)
public void setHandleNativeActionModesEnabled(boolean)
android.view.ViewGroup getSubDecor()
public void setLocalNightMode(int)
final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
private static final boolean DEBUG
private static final java.lang.String KEY_LOCAL_NIGHT_MODE
static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...
Jika Anda ingin melihat laporan titik entri yang ditentukan R8 dari
aturan keep project Anda, sertakan -printseeds <output-dir>/seeds.txt
di
file aturan kustom Anda. Saat Anda mengaktifkan R8 dan membuat aplikasi, R8 akan meng-output
laporan yang berisi jalur dan nama file yang Anda tentukan. Laporan titik entri yang dipertahankan terlihat mirip dengan berikut ini:
com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...
Memecahkan masalah penyingkatan resource
Saat Anda menyingkatkan resource, jendela Build
akan menampilkan ringkasan
resource yang dihapus dari aplikasi. (Anda harus mengklik Toggle view
di sisi kiri jendela terlebih dahulu untuk menampilkan output teks mendetail dari Gradle.) Contoh:
:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning
Gradle juga membuat file diagnostik bernama resources.txt
di
<module-name>/build/outputs/mapping/release/
(folder yang sama
dengan file output ProGuard). File ini menyertakan detail seperti resource
mana yang mereferensikan resource lain, dan resource mana yang digunakan atau
dihapus.
Misalnya, untuk mengetahui alasan @drawable/ic_plus_anim_016
masih berada dalam aplikasi, buka file resources.txt
dan telusuri
nama file tersebut. Anda mungkin mendapati bahwa file itu direferensikan dari resource lain, seperti
berikut:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
Sekarang Anda perlu mengetahui alasan @drawable/add_schedule_fab_icon_anim
dapat diakses—dan jika mencari ke atas, Anda akan menemukan resource tersebut dicantumkan
pada "The root reachable resources are:". Ini berarti terdapat referensi kode ke
add_schedule_fab_icon_anim
(artinya, R.drawable ID-nya ditemukan
dalam kode yang dapat diakses).
Jika Anda tidak menggunakan pemeriksaan ketat, ID resource dapat ditandai sebagai dapat diakses jika ada konstanta string yang tampaknya dapat digunakan untuk membuat nama resource bagi resource yang dimuat secara dinamis. Dalam hal ini, jika mencari output build untuk nama resource tersebut, Anda mungkin menemukan pesan seperti ini:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
used because it format-string matches string pool constant ic_plus_anim_%1$d.
Jika melihat salah satu string ini dan yakin bahwa string tersebut tidak
digunakan untuk memuat resource yang diberikan secara dinamis, Anda dapat menggunakan
atribut tools:discard
untuk memberi tahu sistem build agar menghapusnya,
seperti yang dijelaskan di bagian tentang cara menyesuaikan resource yang perlu dipertahankan.