Menu
R8 Optimized Resource Shrinking di AGP 9: Saat Analisis Statis Bertemu Akses Dinamis

R8 Optimized Resource Shrinking di AGP 9: Saat Analisis Statis Bertemu Akses Dinamis

oleh Nikolay Vlasov

Transisi ke Android Gradle Plugin (AGP) 9.0 memperkenalkan perubahan signifikan dalam cara kerja pengurangan sumber daya (resource shrinking). Ketika isShrinkResources = true diaktifkan di tipe build Anda, AGP kini menggunakan optimized resource shrinking. Mekanisme ini menghubungkan analisis sumber daya secara erat dengan analisis kode, menghapus semua sumber daya yang hanya dirujuk oleh kode yang tidak digunakan. Meskipun hal ini menghasilkan ukuran APK yang lebih kecil, hal ini juga dapat menyebabkan masalah pada proyek yang mengandalkan akses sumber daya dinamis—masalah yang mungkin tidak disadari pada versi sebelumnya. (Android Developers)

Mengapa sumber daya menghilang di Dynamic Feature Modules?

Penyusut sumber daya (resource shrinkers) modern sangat bergantung pada analisis statis. Jika sumber daya dirujuk secara langsung dalam kode Anda (misalnya, R.raw.my_resource), penyusut dapat dengan mudah melacak penggunaannya. Namun, saat Anda mengakses sumber daya secara dinamis menggunakan nama string dan pencarian waktu proses (runtime lookup), analisis statis tidak lagi dapat membuktikan secara andal bahwa sumber daya tersebut diperlukan. Dengan AGP 9, hal ini menjadi lebih nyata karena optimized resource shrinking kini menjadi bagian dari siklus optimasi kode dan sumber daya yang terpadu. (Android Developers)

Dalam proyek yang menggunakan Dynamic Feature Modules (DFM), modul dasar sering kali tidak memiliki dependensi waktu kompilasi pada kelas R fitur tersebut, terutama ketika modul dikirimkan secara terpisah melalui Play Feature Delivery. Meskipun penyusut sumber daya yang baru telah mendukung fitur dinamis sejak lama, arsitektur spesifik inilah yang paling sering menyebabkan masalah "sumber daya hilang" karena pencarian dinamis. (Android Developers)

Ambil contoh cuplikan dari proyek Smart Directory Agent Center. Pemuatan indeks pencarian teks lengkap diimplementasikan sebagai berikut:

private fun getFtsId(): Int {
    return dynamicContextWrapper.getContextForFts().resources.getIdentifier(
        "fts",
        "raw",
        featuresManager.ftsModulePackage,
    )
}

Bagi penyusut sumber daya, panggilan ini adalah "kotak hitam": nama sumber daya diteruskan sebagai string, bukan melalui referensi statis. Akibatnya, alat bantu build mungkin salah menganggap sumber daya tersebut tidak digunakan dan menghapusnya selama build rilis, yang menyebabkan Resources$NotFoundException saat aplikasi dijalankan. (Android Developers)

R8 Optimized Resource Shrinking
Pengurangan sumber daya yang dioptimalkan menghubungkan analisis kode secara erat dengan sumber daya yang tersedia.

Evolusi Resource Shrinking di AGP

Penting untuk dicatat bahwa AGP 9.0 tidak menciptakan kembali resource shrinking dari nol; melainkan membuat perilakunya lebih ketat dan dapat diprediksi dalam siklus optimasi. Precise resource shrinking sudah diaktifkan secara default di AGP 8.3. Di AGP 9.0, optimized resource shrinking dipicu secara otomatis saat isShrinkResources = true. Untuk versi antara 8.6 dan 9.0, perilaku ini biasanya memerlukan flag eksplisit android.r8.optimizedResourceShrinking=true. (Android Developers)

Waspadai regresi di AGP 8.9: catatan rilis mendokumentasikan bug resource shrinking yang menyebabkan hilangnya sumber daya di modul fitur dinamis. Hal ini kemudian diperbaiki pada versi 8.9.2 dan 8.10. Jika Anda mengalami crash pada build rilis Anda, itu mungkin bukan kesalahan arsitektur—bisa jadi itu adalah bug pada versi penyusut tertentu. (Android Developers)

Solusi Nyata: file keep dan Retensi Sumber Daya Eksplisit

Cara yang benar untuk melindungi sumber daya yang digunakan secara tidak langsung adalah dengan menentukan file keep di sumber daya proyek Anda. Dokumentasi merekomendasikan pembuatan file XML di res/raw/ (misalnya, res/raw/my.package.keep.xml) daripada mengandalkan "global keep.xml" generik sebagai mekanisme khusus. Menggunakan nama file yang unik untuk file keep mencegah konflik selama penggabungan sumber daya (resource merging) dalam proyek multi-modul. (Android Developers)

Dalam kasus kami, perbaikannya terlihat seperti ini:

<!-- Jalur: app/src/main/res/raw/com.example.app.keep.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@style/Theme.StartingScreen_*,@style/AppTheme_*,@raw/fts" />

Pendekatan ini memperjelas niat Anda: sumber daya @raw/fts tidak boleh dihapus, meskipun hanya dapat diakses melalui resolusi nama dinamis. Ini sangat penting untuk sumber daya apa pun yang dibaca melalui Resources.getIdentifier(). (Android Developers)

Praktik Terbaik untuk AGP 9 dan R8

Pertama, untuk sumber daya yang diakses secara dinamis, sebaiknya cantumkan secara eksplisit di tools:keep daripada mengandalkan wildcard luas seperti @raw/*. Semakin spesifik aturan Anda, semakin kecil kemungkinan Anda menyimpan sumber daya yang tidak perlu, dan semakin dapat diprediksi perilaku build Anda. (Android Developers)

Kedua, perhatikan shrinkMode. Secara default, safe mode digunakan, yang mencoba mempertahankan sumber daya yang mungkin digunakan secara dinamis melalui getIdentifier(). Jika Anda beralih ke tools:shrinkMode="strict", hanya sumber daya yang dirujuk secara eksplisit yang akan dipertahankan, sehingga keakuratan aturan keep Anda menjadi sangat penting. (Android Developers)

Ketiga, jangan bingung membedakan antara penyebab dan gejala. Masalahnya bukan karena proyek Anda menggunakan DFM; masalahnya adalah beberapa sumber daya hanya dapat dijangkau melalui pencarian waktu kerja dan tetap tidak terlihat oleh analisis statis. DFM hanya membuat skenario ini lebih umum dan lebih terlihat selama optimasi. (Android Developers)

Ringkasan

Sumber daya yang hilang di AGP 9 bukanlah hasil dari keajaiban atau kesalahan acak. Itu adalah konsekuensi logis dari analisis statis yang lebih ketat dalam optimized resource shrinking. Jika arsitektur Anda mengandalkan akses sumber daya dinamis, Anda harus secara eksplisit melindungi sumber daya tersebut menggunakan file keep dan memverifikasi perilakunya terhadap mode penyusut yang Anda pilih. Jika crash muncul secara khusus setelah memperbarui ke AGP 8.9–9.0, periksa kembali versi plugin Anda, karena regresi yang diketahui di area ini telah diatasi dalam patch berikutnya. (Android Developers)

Sumber Daya Bermanfaat