Android 12 memiliki perubahan sistem build pada kompilasi AOT file DEX (dexpreopt) untuk modul Java yang memiliki dependensi <uses-library>
. Dalam beberapa kasus, perubahan sistem build ini dapat merusak build. Gunakan halaman ini untuk bersiap menghadapi kerusakan, dan ikuti resep di halaman ini untuk memperbaiki dan menguranginya.
Dexpreopt adalah proses kompilasi perpustakaan dan aplikasi Java sebelumnya. Dexpreopt terjadi di host pada waktu build (berbeda dengan dexopt , yang terjadi di perangkat). Struktur dependensi perpustakaan bersama yang digunakan oleh modul Java (perpustakaan atau aplikasi) dikenal sebagai konteks pemuat kelas (CLC). Untuk menjamin kebenaran dexpreopt, CLC waktu pembuatan dan waktu proses harus bersamaan. CLC waktu pembuatan adalah apa yang digunakan oleh kompiler dex2oat pada waktu dexpreopt (dicatat dalam file ODEX), dan CLC waktu proses adalah konteks di mana kode yang telah dikompilasi dimuat pada perangkat.
CLC waktu pembuatan dan waktu proses ini harus bertepatan karena alasan kebenaran dan kinerja. Untuk kebenarannya, penting untuk menangani kelas duplikat. Jika dependensi pustaka bersama saat waktu proses berbeda dengan yang digunakan untuk kompilasi, beberapa kelas mungkin diselesaikan secara berbeda, sehingga menyebabkan bug waktu proses yang tidak kentara. Kinerja juga dipengaruhi oleh pemeriksaan runtime untuk kelas duplikat.
Kasus penggunaan yang terpengaruh
Boot pertama adalah kasus penggunaan utama yang dipengaruhi oleh perubahan ini: jika ART mendeteksi ketidakcocokan antara CLC waktu build dan waktu proses, ART akan menolak artefak dexpreopt dan menjalankan dexopt. Untuk boot berikutnya, ini baik-baik saja karena aplikasi dapat didesoptasi di latar belakang dan disimpan di disk.
Area Android yang terkena dampak
Hal ini mempengaruhi semua aplikasi dan perpustakaan Java yang memiliki ketergantungan runtime pada perpustakaan Java lainnya. Android memiliki ribuan aplikasi, dan ratusan di antaranya menggunakan perpustakaan bersama. Mitra juga terkena dampaknya, karena mereka memiliki perpustakaan dan aplikasi sendiri.
Hancurkan perubahan
Sistem pembangunan perlu mengetahui dependensi <uses-library>
sebelum menghasilkan aturan pembangunan dexpreopt. Namun, sistem build tidak dapat mengakses manifes secara langsung dan membaca tag <uses-library>
di dalamnya, karena sistem build tidak diperbolehkan membaca file sembarangan saat membuat aturan build (untuk alasan performa). Selain itu, manifesnya mungkin dikemas dalam APK atau bawaan. Oleh karena itu, informasi <uses-library>
harus ada dalam file build ( Android.bp
atau Android.mk
).
Sebelumnya ART menggunakan solusi yang mengabaikan dependensi perpustakaan bersama (dikenal sebagai &-classpath
). Ini tidak aman dan menyebabkan bug halus, jadi solusinya telah dihapus di Android 12.
Akibatnya, modul Java yang tidak memberikan informasi <uses-library>
yang benar dalam file build-nya dapat menyebabkan kerusakan build (disebabkan oleh ketidakcocokan CLC waktu build) atau regresi waktu boot pertama (disebabkan oleh CLC waktu boot ketidakcocokan diikuti oleh dexopt).
Jalur migrasi
Ikuti langkah-langkah berikut untuk memperbaiki build yang rusak:
Nonaktifkan pemeriksaan waktu pembuatan secara global untuk produk tertentu berdasarkan pengaturan
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
di makefile produk. Ini memperbaiki kesalahan build (kecuali untuk kasus khusus, yang tercantum di bagian Memperbaiki kerusakan ). Namun, ini adalah solusi sementara, dan dapat menyebabkan ketidakcocokan CLC waktu boot yang diikuti oleh dexopt.
Perbaiki modul yang gagal sebelum Anda menonaktifkan pemeriksaan waktu build secara global dengan menambahkan informasi
<uses-library>
yang diperlukan ke file build-nya (lihat Memperbaiki kerusakan untuk detailnya). Untuk sebagian besar modul, hal ini memerlukan penambahan beberapa baris diAndroid.bp
, atau diAndroid.mk
.Nonaktifkan pemeriksaan waktu build dan dexpreopt untuk kasus yang bermasalah, per modul. Nonaktifkan dexpreopt agar Anda tidak membuang waktu pembuatan dan penyimpanan pada artefak yang ditolak saat boot.
Aktifkan kembali pemeriksaan waktu pembuatan secara global dengan membatalkan pengaturan
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
yang ditetapkan pada Langkah 1; build tidak akan gagal setelah perubahan ini (karena langkah 2 dan 3).Perbaiki modul yang Anda nonaktifkan pada Langkah 3, satu per satu, lalu aktifkan kembali dexpreopt dan centang
<uses-library>
. Ajukan bug jika perlu.
Pemeriksaan <uses-library>
waktu build diterapkan di Android 12.
Perbaiki kerusakan
Bagian berikut ini memberi tahu Anda cara memperbaiki jenis kerusakan tertentu.
Kesalahan pembuatan: ketidakcocokan CLC
Sistem build melakukan pemeriksaan koherensi waktu build antara informasi dalam file Android.bp
atau Android.mk
dan manifes. Sistem build tidak dapat membaca manifes, namun dapat menghasilkan aturan build untuk membaca manifes (mengekstraknya dari APK jika perlu), dan membandingkan tag <uses-library>
di manifes dengan informasi <uses-library>
di file pembangunan. Jika pemeriksaan gagal, kesalahannya terlihat seperti ini:
error: mismatch in the <uses-library> tags between the build system and the manifest:
- required libraries in build system: []
vs. in the manifest: [org.apache.http.legacy]
- optional libraries in build system: []
vs. in the manifest: [com.x.y.z]
- tags in the manifest (.../X_intermediates/manifest/AndroidManifest.xml):
<uses-library android:name="com.x.y.z"/>
<uses-library android:name="org.apache.http.legacy"/>
note: the following options are available:
- to temporarily disable the check on command line, rebuild with RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)
- to temporarily disable the check for the whole product, set PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles
- to fix the check, make build system properties coherent with the manifest
- see build/make/Changes.md for details
Seperti yang disarankan dalam pesan kesalahan, ada beberapa solusi, bergantung pada urgensinya:
- Untuk perbaikan sementara seluruh produk , setel
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
di makefile produk. Pemeriksaan koherensi waktu pembangunan masih dilakukan, namun kegagalan pemeriksaan tidak berarti kegagalan pembangunan. Sebaliknya, kegagalan pemeriksaan membuat sistem build menurunkan versi filter kompiler dex2oat untukverify
di dexpreopt, yang menonaktifkan kompilasi AOT sepenuhnya untuk modul ini. - Untuk perbaikan baris perintah global yang cepat , gunakan variabel lingkungan
RELAX_USES_LIBRARY_CHECK=true
. Ini memiliki efek yang sama sepertiPRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, namun dimaksudkan untuk digunakan pada baris perintah. Variabel lingkungan menggantikan variabel produk. - Untuk solusi memperbaiki akar penyebab kesalahan, buat sistem build mengetahui tag
<uses-library>
dalam manifes. Pemeriksaan pesan kesalahan menunjukkan perpustakaan mana yang menyebabkan masalah (seperti halnya pemeriksaanAndroidManifest.xml
atau manifes di dalam APK yang dapat diperiksa dengan `aapt dump badging $APK | grep uses-library
`).
Untuk modul Android.bp
:
Cari perpustakaan yang hilang di properti
libs
modul. Jika ada, Soong biasanya menambahkan perpustakaan tersebut secara otomatis, kecuali dalam kasus khusus berikut:- Pustaka tersebut bukan pustaka SDK (didefinisikan sebagai
java_library
, bukanjava_sdk_library
). - Perpustakaan memiliki nama perpustakaan yang berbeda (dalam manifes) dari nama modulnya (dalam sistem pembangunan).
Untuk memperbaikinya sementara, tambahkan
provides_uses_lib: "<library-name>"
dalam definisi perpustakaanAndroid.bp
. Untuk solusi jangka panjang, perbaiki masalah mendasar: konversikan pustaka menjadi pustaka SDK, atau ganti nama modulnya.- Pustaka tersebut bukan pustaka SDK (didefinisikan sebagai
Jika langkah sebelumnya tidak memberikan resolusi, tambahkan
uses_libs: ["<library-module-name>"]
untuk pustaka yang diperlukan, atauoptional_uses_libs: ["<library-module-name>"]
untuk pustaka opsional keAndroid.bp
definisiAndroid.bp
dari modul. Properti ini menerima daftar nama modul. Urutan relatif perpustakaan dalam daftar harus sama dengan urutan dalam manifes.
Untuk modul Android.mk
:
Periksa apakah perpustakaan memiliki nama perpustakaan yang berbeda (dalam manifes) dari nama modulnya (dalam sistem pembangunan). Jika ya, perbaiki sementara dengan menambahkan
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
di fileAndroid.mk
perpustakaan, atau tambahkanprovides_uses_lib: "<library-name>"
di fileAndroid.bp
perpustakaan (keduanya kasus dimungkinkan karena modulAndroid.mk
mungkin bergantung pada perpustakaanAndroid.bp
). Untuk solusi jangka panjang, perbaiki masalah mendasar: ganti nama modul perpustakaan.Tambahkan
LOCAL_USES_LIBRARIES := <library-module-name>
untuk perpustakaan yang diperlukan; tambahkanLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
untuk pustaka opsional ke definisi modulAndroid.mk
. Properti ini menerima daftar nama modul. Urutan relatif perpustakaan dalam daftar harus sama dengan yang ada di manifes.
Kesalahan build: jalur perpustakaan tidak diketahui
Jika sistem pembangunan tidak dapat menemukan jalur ke toples DEX <uses-library>
(baik jalur waktu pembangunan di host atau jalur pemasangan di perangkat), biasanya sistem tersebut gagal dalam pembangunan. Kegagalan menemukan jalur dapat menunjukkan bahwa perpustakaan dikonfigurasi dengan cara yang tidak terduga. Perbaiki sementara build dengan menonaktifkan dexpreopt untuk modul yang bermasalah.
Android.bp (properti modul):
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (variabel modul):
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Ajukan bug untuk menyelidiki skenario yang tidak didukung.
Kesalahan build: ketergantungan perpustakaan hilang
Upaya untuk menambahkan <uses-library>
X dari manifes modul Y ke file build untuk Y mungkin mengakibatkan kesalahan build karena ketergantungan yang hilang, X.
Ini adalah contoh pesan kesalahan untuk modul Android.bp:
"Y" depends on undefined module "X"
Ini adalah contoh pesan kesalahan untuk modul Android.mk:
'.../JAVA_LIBRARIES/com.android.X_intermediates/dexpreopt.config', needed by '.../APPS/Y_intermediates/enforce_uses_libraries.status', missing and no known rule to make it
Sumber umum kesalahan tersebut adalah ketika perpustakaan diberi nama berbeda dari nama modul terkait dalam sistem pembangunan. Misalnya, jika entri manifes <uses-library>
adalah com.android.X
, namun nama modul perpustakaannya hanya X
, hal ini akan menyebabkan kesalahan. Untuk mengatasi kasus ini, beri tahu sistem build bahwa modul bernama X
menyediakan <uses-library>
bernama com.android.X
.
Ini adalah contoh untuk perpustakaan Android.bp
(properti modul):
provides_uses_lib: “com.android.X”,
Ini adalah contoh untuk perpustakaan Android.mk (variabel modul):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Ketidakcocokan CLC waktu boot
Saat boot pertama, cari logcat untuk pesan terkait ketidakcocokan CLC, seperti yang ditunjukkan di bawah ini:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
Outputnya dapat berupa pesan seperti yang ditunjukkan di sini:
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Jika Anda mendapatkan peringatan ketidakcocokan CLC, cari perintah dexopt untuk modul yang salah. Untuk memperbaikinya, pastikan pemeriksaan waktu build untuk modul berhasil. Jika tidak berhasil, kasus Anda mungkin merupakan kasus khusus yang tidak didukung oleh sistem build (seperti aplikasi yang memuat APK lain, bukan perpustakaan). Sistem pembangunan tidak menangani semua kasus, karena pada waktu pembangunan tidak mungkin mengetahui secara pasti apa yang dimuat aplikasi pada waktu proses.
Konteks pemuat kelas
CLC adalah struktur seperti pohon yang menggambarkan hierarki pemuat kelas. Sistem pembangunan menggunakan CLC dalam arti sempit (hanya mencakup pustaka, bukan APK atau pemuat kelas khusus): ini adalah pohon pustaka yang mewakili penutupan transitif semua dependensi <uses-library>
pada pustaka atau aplikasi. Elemen tingkat atas dari CLC adalah dependensi langsung <uses-library>
yang ditentukan dalam manifes (classpath). Setiap node pada pohon CLC adalah node <uses-library>
yang mungkin memiliki sub-node <uses-library>
sendiri.
Karena dependensi <uses-library>
adalah grafik asiklik terarah, dan belum tentu berupa pohon, CLC dapat berisi beberapa subpohon untuk perpustakaan yang sama. Dengan kata lain, CLC adalah grafik ketergantungan yang "dibuka" ke pohon. Duplikasinya hanya pada tingkat logis; pemuat kelas sebenarnya yang mendasarinya tidak diduplikasi (saat runtime ada satu instance pemuat kelas untuk setiap perpustakaan).
CLC mendefinisikan urutan pencarian perpustakaan saat menyelesaikan kelas Java yang digunakan oleh perpustakaan atau aplikasi. Urutan pencarian penting karena perpustakaan dapat berisi kelas duplikat, dan kelas tersebut diselesaikan pada kecocokan pertama.
Pada perangkat (run-time) CLC
PackageManager
(dalam frameworks/base
) membuat CLC untuk memuat modul Java di perangkat. Ia menambahkan perpustakaan yang tercantum dalam tag <uses-library>
dalam manifes modul sebagai elemen CLC tingkat atas.
Untuk setiap pustaka yang digunakan, PackageManager
mendapatkan semua dependensi <uses-library>
(ditentukan sebagai tag dalam manifes pustaka tersebut) dan menambahkan CLC bertingkat untuk setiap dependensi. Proses ini berlanjut secara rekursif hingga semua node daun dari pohon CLC yang dibangun adalah perpustakaan tanpa ketergantungan <uses-library>
.
PackageManager
hanya mengetahui perpustakaan bersama. Definisi shared dalam penggunaan ini berbeda dari arti biasanya (seperti dalam shared vs. static). Di Android, pustaka bersama Java adalah pustaka yang tercantum dalam konfigurasi XML yang diinstal pada perangkat ( /system/etc/permissions/platform.xml
). Setiap entri berisi nama perpustakaan bersama, jalur ke file jar DEX-nya, dan daftar dependensi (perpustakaan bersama lainnya yang digunakan saat runtime, dan ditentukan dalam tag <uses-library>
dalam manifesnya).
Dengan kata lain, ada dua sumber informasi yang memungkinkan PackageManager
membuat CLC saat runtime: tag <uses-library>
di manifes, dan dependensi pustaka bersama di konfigurasi XML.
CLC di host (waktu pembuatan).
CLC tidak hanya diperlukan saat memuat perpustakaan atau aplikasi, tetapi juga diperlukan saat mengkompilasinya. Kompilasi dapat dilakukan di perangkat (dexopt) atau selama build (dexpreopt). Karena dexopt berlangsung di perangkat, ia memiliki informasi yang sama dengan PackageManager
(manifes dan dependensi pustaka bersama). Namun, Dexpreopt dilakukan di host dan di lingkungan yang sama sekali berbeda, dan harus mendapatkan informasi yang sama dari sistem build.
Jadi, CLC waktu pembuatan yang digunakan oleh dexpreopt dan CLC waktu proses yang digunakan oleh PackageManager
adalah hal yang sama, tetapi dihitung dengan dua cara berbeda.
CLC waktu pembuatan dan waktu proses harus bertepatan, jika tidak, kode kompilasi AOT yang dibuat oleh dexpreopt akan ditolak. Untuk memeriksa kesetaraan CLC waktu pembangunan dan waktu proses, kompiler dex2oat mencatat CLC waktu pembangunan dalam file *.odex
(di bidang classpath
pada header file OAT). Untuk menemukan CLC yang disimpan, gunakan perintah ini:
oatdump --oat-file=<FILE> | grep '^classpath = '
Ketidakcocokan CLC waktu pembuatan dan waktu proses dilaporkan di logcat saat boot. Cari dengan perintah ini:
logcat | grep -E 'ClassLoaderContext [az ]+ mismatch'
Ketidakcocokan berdampak buruk bagi kinerja, karena memaksa perpustakaan atau aplikasi untuk dihapus, atau dijalankan tanpa pengoptimalan (misalnya, kode aplikasi mungkin perlu diekstraksi ke dalam memori dari APK, sebuah operasi yang sangat mahal).
Perpustakaan bersama dapat bersifat opsional atau wajib. Dari sudut pandang dexpreopt, perpustakaan yang diperlukan harus ada pada waktu pembangunan (ketidakhadirannya merupakan kesalahan pembangunan). Pustaka opsional dapat ada atau tidak ada pada waktu pembuatan: jika ada, pustaka tersebut akan ditambahkan ke CLC, diteruskan ke dex2oat, dan dicatat dalam file *.odex
. Jika perpustakaan opsional tidak ada, perpustakaan itu dilewati dan tidak ditambahkan ke CLC. Jika ada ketidakcocokan antara status waktu pembangunan dan waktu proses (perpustakaan opsional ada dalam satu kasus, namun tidak dalam kasus lainnya), maka CLC waktu pembangunan dan waktu proses tidak cocok dan kode yang dikompilasi akan ditolak.
Detail sistem build tingkat lanjut (pemecah masalah manifes)
Terkadang tag <uses-library>
hilang dari manifes sumber perpustakaan atau aplikasi. Hal ini dapat terjadi, misalnya, jika salah satu dependensi transitif perpustakaan atau aplikasi mulai menggunakan tag <uses-library>
lain, dan manifes perpustakaan atau aplikasi tidak diperbarui untuk menyertakan tag tersebut.
Soong dapat menghitung beberapa tag <uses-library>
yang hilang untuk pustaka atau aplikasi tertentu secara otomatis, sebagai pustaka SDK dalam penutupan ketergantungan transitif pustaka atau aplikasi. Penutupan diperlukan karena perpustakaan (atau aplikasi) mungkin bergantung pada perpustakaan statis yang bergantung pada perpustakaan SDK, dan mungkin lagi bergantung secara transitif melalui perpustakaan lain.
Tidak semua tag <uses-library>
dapat dihitung dengan cara ini, namun bila memungkinkan, lebih baik membiarkan Soong menambahkan entri manifes secara otomatis; ini tidak terlalu rawan kesalahan dan menyederhanakan pemeliharaan. Misalnya, ketika banyak aplikasi menggunakan perpustakaan statis yang menambahkan dependensi <uses-library>
baru, semua aplikasi harus diperbarui, sehingga sulit untuk dikelola.