Android 12 memiliki perubahan sistem build pada kompilasi AOT
file DEX (dexpreopt) untuk modul Java yang memiliki <uses-library>
dependensi. Dalam beberapa kasus, perubahan sistem build ini dapat merusak
build. Gunakan halaman ini untuk bersiap menghadapi kerusakan, dan ikuti petunjuk di halaman ini untuk memperbaiki dan menguranginya.
Dexpreopt adalah proses kompilasi library Java dan aplikasi. Dexpreopt terjadi di host pada waktu build (bukan dexopt, yang terjadi di perangkat). Struktur dependensi library bersama yang digunakan oleh modul Java (library atau aplikasi) dikenal sebagai konteks loader class (CLC). Untuk memastikan kebenaran dexpreopt, CLC waktu build dan waktu proses harus bersamaan. CLC waktu build adalah yang digunakan compiler dex2oat pada waktu dexpreopt (direkam dalam file ODEX), dan CLC {i>run-time<i} adalah konteks di mana kode prakompilasi dimuat di perangkat.
CLC waktu build dan waktu proses ini harus bertepatan karena kedua alasan tersebut dan performa. Demi ketepatan, Anda perlu menangani class duplikat. Jika dependensi library bersama saat runtime berbeda dengan yang digunakan untuk kompilasi, beberapa class mungkin diselesaikan secara berbeda, sehingga menyebabkan {i>bug runtime<i} lainnya. Performa juga dipengaruhi oleh pemeriksaan duplikasi oleh runtime Google Cloud Platform.
Kasus penggunaan yang terpengaruh
Booting pertama adalah kasus penggunaan utama yang terpengaruh oleh perubahan ini: jika ART mendeteksi ketidakcocokan antara CLC waktu build dan waktu proses, CLC menolak dexpreopt artefak dan menjalankan {i> dexopt<i}. Untuk {i>booting<i} berikutnya tidak apa-apa karena aplikasi dapat di-dex di latar belakang dan disimpan di {i>disk<i}.
Area Android yang terpengaruh
Hal ini memengaruhi semua aplikasi dan library Java yang memiliki dependensi runtime pada library Java lainnya. Android memiliki ribuan aplikasi, dan ratusan di antaranya menggunakan perpustakaan bersama. Partner juga akan terpengaruh, karena mereka memiliki library dan aplikasi.
Perubahan yang dapat menyebabkan gangguan
Sistem build perlu mengetahui dependensi <uses-library>
sebelum hal tersebut
menghasilkan aturan build dexpreopt. Akan tetapi, tindakan ini tidak dapat mengakses manifes secara langsung
dan membaca <uses-library>
tag di dalamnya, karena sistem build tidak diizinkan membaca file arbitrer saat
proses ini menghasilkan aturan build (untuk alasan performa). Selain itu, manifes mungkin
dikemas di dalam APK atau aplikasi bawaan. Oleh karena itu, <uses-library>
informasi harus ada di file build (Android.bp
atau Android.mk
).
Sebelumnya, ART menggunakan solusi yang mengabaikan dependensi library bersama (dikenal
sebagai &-classpath
). Hal ini tidak aman dan menyebabkan bug halus, sehingga solusi
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 booting pertama (disebabkan oleh ketidakcocokan
CLC waktu booting yang diikuti dengan dexopt).
Jalur migrasi
Ikuti langkah-langkah berikut untuk memperbaiki build yang rusak:
Nonaktifkan pemeriksaan waktu build secara global untuk produk tertentu dengan menyetel
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
dalam makefile produk. Tindakan ini akan memperbaiki error build (kecuali untuk kasus khusus, yang tercantum di bagian Memperbaiki kerusakan). Namun, ini adalah solusi sementara, dan dapat menyebabkan ketidakcocokan CLC waktu {i>booting<i} diikuti oleh {i>dexopt<i}.
Memperbaiki 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 mengetahui detailnya). Untuk sebagian besar modul, hal ini memerlukan penambahan beberapa baris diAndroid.bp
, atau diAndroid.mk
.Untuk kasus yang bermasalah, nonaktifkan pemeriksaan waktu build dan dexpreopt. per modul. Nonaktifkan dexpreopt sehingga Anda tidak membuang waktu build dan penyimpanan pada artefak yang ditolak saat {i>booting<i}.
Aktifkan kembali pemeriksaan waktu build secara global dengan menghapus
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
yang ditetapkan di Langkah 1; build tidak boleh gagal setelah perubahan ini (karena langkah 2 dan 3).Perbaiki modul yang Anda nonaktifkan di Langkah 3, satu per satu, lalu aktifkan kembali dexpreopt dan pemeriksaan
<uses-library>
. Laporkan bug jika perlu.
Pemeriksaan <uses-library>
waktu build diterapkan di Android 12.
Memperbaiki kerusakan
Bagian berikut memberi tahu Anda cara memperbaiki jenis kerusakan tertentu.
Error build: 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, tetapi dapat menghasilkan aturan build untuk membaca manifes (mengekstrak
dari APK jika perlu), dan bandingkan tag <uses-library>
dalam manifes
terhadap informasi <uses-library>
dalam file build. Jika pemeriksaan gagal,
error akan 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 pesan error, ada beberapa solusi, bergantung pada urgensi:
- Untuk perbaikan sementara di seluruh produk, tetapkan
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
di makefile produk. Pemeriksaan koherensi waktu build masih dilakukan, tetapi kegagalan pemeriksaan tidak berarti kegagalan build. Sebagai gantinya, kegagalan pemeriksaan membuat sistem build mendowngrade Filter compiler dex2oat keverify
di dexpreopt, yang menonaktifkan kompilasi AOT sepenuhnya untuk modul ini. - Untuk perbaikan command line global yang cepat, gunakan variabel lingkungan
RELAX_USES_LIBRARY_CHECK=true
. Hal ini memiliki efek yang sama denganPRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, tetapi dimaksudkan untuk digunakan pada command line. Variabel lingkungan akan menggantikan variabel produk. - Sebagai solusi untuk memperbaiki akar masalah error, buat sistem build mengetahui
tag
<uses-library>
dalam manifes. Pemeriksaan pesan error menunjukkan library mana yang menyebabkan masalah (seperti memeriksaAndroidManifest.xml
atau manifes di dalam APK yang dapat diperiksa dengan `aapt dump badging $APK | grep uses-library
`).
Untuk modul Android.bp
:
Cari library yang hilang di properti
libs
modul. Jika ada, Soong biasanya menambahkan library tersebut secara otomatis, kecuali dalam kasus khusus ini:- Library ini bukan library SDK (ditetapkan sebagai
java_library
, bukan daripadajava_sdk_library
). - Library memiliki nama library yang berbeda (dalam manifes) dari nama modulnya (dalam sistem build).
Untuk memperbaikinya sementara waktu, tambahkan
provides_uses_lib: "<library-name>"
di Definisi libraryAndroid.bp
. Untuk solusi jangka panjang, perbaiki masalah masalah: mengonversi library menjadi library SDK, atau mengganti nama modulnya.- Library ini bukan library SDK (ditetapkan sebagai
Jika langkah sebelumnya tidak memberikan resolusi, tambahkan
uses_libs: ["<library-module-name>"]
untuk library yang diperlukan, atauoptional_uses_libs: ["<library-module-name>"]
untuk library opsional ke definisiAndroid.bp
modul. Properti ini menerima daftar nama modul. Urutan relatif library dalam daftar harus sama sebagai urutan dalam manifes.
Untuk modul Android.mk
:
Periksa apakah library memiliki nama library yang berbeda (dalam manifes) dari nama modul (dalam sistem build). Jika ya, perbaiki untuk sementara dengan menambahkan
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
dalam fileAndroid.mk
library, atau tambahkanprovides_uses_lib: "<library-name>"
dalam fileAndroid.bp
library (keduanya mungkin karena modulAndroid.mk
mungkin bergantung pada libraryAndroid.bp
). Untuk solusi jangka panjang, perbaiki masalah yang mendasarinya: ganti nama modul library.Tambahkan
LOCAL_USES_LIBRARIES := <library-module-name>
untuk kolom yang wajib diisi {i>library<i}; tambahkanLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
untuk library opsional ke definisiAndroid.mk
modul. Properti ini menerima daftar nama modul. Urutan relatif perpustakaan di daftar harus sama dengan yang ada di manifes.
Error build: jalur library tidak diketahui
Jika sistem build tidak dapat menemukan jalur ke jar DEX <uses-library>
(baik
jalur waktu build di host maupun jalur penginstalan di perangkat), build biasanya akan gagal. Kegagalan untuk menemukan jalur dapat menunjukkan bahwa library dikonfigurasi dengan
cara yang tidak terduga. Perbaiki build untuk sementara 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
Laporkan bug untuk menyelidiki skenario yang tidak didukung.
Error build: dependensi library tidak ada
Upaya untuk menambahkan <uses-library>
X dari manifes modul Y ke build
untuk Y dapat mengakibatkan error build karena tidak adanya dependensi, X.
Ini adalah contoh pesan error untuk modul Android.bp:
"Y" depends on undefined module "X"
Berikut adalah contoh pesan error 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 error tersebut adalah saat library diberi nama yang berbeda dengan
nama modul yang sesuai di sistem build. Misalnya, jika manifes
Entri <uses-library>
adalah com.android.X
, tetapi nama modul library ini
hanya X
, ini akan menyebabkan error. Untuk mengatasi kasus ini, beri tahu sistem build bahwa
modul bernama X
menyediakan <uses-library>
bernama com.android.X
.
Ini adalah contoh untuk library Android.bp
(properti modul):
provides_uses_lib: “com.android.X”,
Ini adalah contoh untuk library Android.mk (variabel modul):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
CLC waktu booting tidak cocok
Saat pertama kali melakukan booting, telusuri logcat untuk menemukan pesan yang terkait dengan ketidakcocokan CLC, seperti yang ditunjukkan di bawah:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
Output dapat memiliki pesan dalam bentuk yang ditampilkan 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 error ruang lingkup modul ini. Untuk memperbaikinya, pastikan pemeriksaan waktu build untuk modul lulus. Jika itu tidak berhasil, maka kasus Anda mungkin merupakan kasus khusus yang tidak didukung oleh sistem build (seperti aplikasi yang memuat APK lain, bukan library). Tujuan sistem build tidak menangani semua kasus, karena pada waktu pembangunan tidak mungkin untuk mengetahui secara pasti apa yang dimuat aplikasi pada saat {i>runtime<i}.
Konteks loader class
CLC adalah struktur seperti hierarki yang menjelaskan hierarki class loader. Sistem
build menggunakan CLC dalam arti sempit (hanya mencakup library, bukan APK atau
loader class kustom): ini adalah hierarki library yang mewakili penutupan
transitif dari semua dependensi <uses-library>
library atau aplikasi. Elemen
level atas CLC adalah dependensi <uses-library>
langsung yang ditentukan
dalam manifes (classpath). Setiap node dari hierarki CLC adalah
node <uses-library>
yang mungkin memiliki sub-node <uses-library>
-nya sendiri.
Karena dependensi <uses-library>
adalah grafik acyclic terarah, dan tidak
harus berupa hierarki, CLC dapat berisi beberapa subhierarki untuk library yang sama. Di beberapa
dengan kata lain, CLC adalah grafik
dependensi "{i>unfolded<i}" pada pohon. Duplikat
hanya pada tingkat yang logis; loader kelas pokok yang sesungguhnya bukanlah
diduplikasi (saat runtime ada satu instance loader class untuk setiap library).
CLC mendefinisikan urutan pencarian library saat me-resolve class Java yang digunakan oleh {i>library<i} atau aplikasi. Urutan pencarian penting karena {i>library<i} dapat berisi class duplikat, dan class diselesaikan ke kecocokan pertama.
CLC di perangkat (waktu proses)
PackageManager
(di frameworks/base
) membuat CLC untuk memuat modul Java
di perangkat. Tindakan ini akan menambahkan library yang tercantum dalam tag <uses-library>
dalam manifes
modul sebagai elemen CLC level teratas.
Untuk setiap library yang digunakan, PackageManager
mendapatkan semua <uses-library>
-nya
dependensi (ditentukan sebagai tag dalam manifes library tersebut) dan menambahkan sebuah
CLC bersarang untuk setiap dependensi. Proses ini berlanjut secara rekursif hingga semua
node daun dari hierarki CLC yang dibuat adalah library tanpa dependensi
<uses-library>
.
PackageManager
hanya mengetahui library bersama. Definisi dibagikan dalam
penggunaan ini berbeda dengan makna biasanya (seperti dibagikan vs. statis). Di Android,
Library bersama Java adalah yang tercantum dalam konfigurasi XML yang diinstal
di perangkat (/system/etc/permissions/platform.xml
). Setiap entri berisi nama
dari library bersama, jalur ke file jar DEX-nya, dan daftar dependensi
(pustaka bersama lain yang digunakannya
pada waktu proses, dan ditentukan di
<uses-library>
dalam manifesnya).
Dengan kata lain, ada dua sumber informasi yang memungkinkan PackageManager
untuk membuat CLC saat runtime: tag <uses-library>
di manifes, dan
dependensi library bersama dalam konfigurasi XML.
CLC di host (waktu build)
CLC tidak hanya diperlukan saat memuat {i>library<i} atau aplikasi, tetapi juga diperlukan saat
dengan mengompilasi satu file. Kompilasi dapat terjadi di perangkat (dexopt) atau selama
build (dexpreopt). Karena dexopt berlangsung di perangkat, dexopt memiliki
informasi sebagai PackageManager
(manifes dan dependensi library bersama).
Namun, Dexpreopt terjadi di host dan di tempat yang
dan harus mendapatkan informasi yang sama dari sistem build.
Dengan demikian, CLC waktu build yang digunakan oleh dexpreopt dan CLC waktu proses
PackageManager
adalah hal yang sama, tetapi dikomputasi dengan dua cara berbeda.
CLC waktu build dan runtime harus bertepatan, jika tidak, kode yang dikompilasi AOT
yang dibuat oleh dexpreopt akan ditolak. Untuk memeriksa kesetaraan waktu build dan
CLC runtime, compiler dex2oat mencatat CLC waktu build dalam file *.odex
(di kolom classpath
pada header file OAT). Untuk menemukan CLC yang tersimpan, gunakan
perintah berikut:
oatdump --oat-file=<FILE> | grep '^classpath = '
Ketidakcocokan CLC waktu build dan waktu proses dilaporkan di logcat selama booting. Telusuri dengan perintah ini:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
Ketidakcocokan akan berdampak buruk pada performa, karena memaksa library atau aplikasi untuk di-dexopt, atau berjalan tanpa pengoptimalan (misalnya, kode aplikasi mungkin perlu diekstrak dalam memori dari APK, yang merupakan operasi yang sangat mahal).
Library bersama dapat bersifat opsional atau wajib. Dari
dari sudut pandang dexpreopt, library yang diperlukan harus ada pada waktu build
tidak ada, adalah error build). Library opsional dapat ada atau tidak ada
saat waktu build: jika ada, library akan ditambahkan ke CLC, diteruskan ke dex2oat, dan
direkam dalam file *.odex
. Jika library opsional tidak ada, library akan dilewati
dan tidak ditambahkan ke CLC. Jika ada ketidakcocokan antara status waktu build dan
waktu proses (library opsional ada dalam satu kasus, tetapi tidak ada dalam kasus lainnya),
CLC waktu build dan waktu proses tidak cocok dan kode yang dikompilasi akan
ditolak.
Detail sistem build lanjutan (perbaikan manifes)
Terkadang tag <uses-library>
tidak ada di manifes sumber
library atau aplikasi. Hal ini dapat terjadi, misalnya, jika salah satu
dependensi transitif
library atau aplikasi mulai menggunakan tag <uses-library>
lain, dan
{i>library<i} atau manifes aplikasi tidak
diperbarui untuk menyertakannya.
Soong dapat menghitung beberapa tag <uses-library>
yang hilang untuk library
atau aplikasi tertentu secara otomatis, seperti library SDK dalam penutupan dependensi transitif
library atau aplikasi. Penutupan diperlukan karena library (atau aplikasi) mungkin
bergantung pada library statis yang bergantung pada library SDK, dan mungkin
lagi bergantung secara transitif melalui library lain.
Tidak semua tag <uses-library>
dapat dihitung dengan cara ini, tetapi jika memungkinkan, sebaiknya
biarkan Soong menambahkan entri manifes secara otomatis; lebih sedikit
rentan error dan menyederhanakan pemeliharaan. Misalnya, saat banyak aplikasi menggunakan library statis
yang menambahkan dependensi <uses-library>
baru, semua aplikasi harus
diupdate, yang sulit dikelola.