Dexpreopt ve <uses-library> kontrolleri

Android 12, <uses-library> bağımlılıkları olan Java modülleri için DEX dosyalarının (dexpreopt) AOT derlemesiyle ilgili derleme sistemi değişiklikleri içerir. Bazı durumlarda bu derleme sistemi değişiklikleri derlemelerin bozulmasına neden olabilir. Bu sayfayı kullanarak kırılmalara hazırlanabilir, bu sayfadaki tarifleri uygulayarak kırılmaları düzeltebilir ve etkilerini azaltabilirsiniz.

Dexpreopt, Java kitaplıklarının ve uygulamalarının önceden derlenmesi işlemidir. Dexpreopt, derleme sırasında ana makinede gerçekleşir (cihazda gerçekleşen dexopt'un aksine). Bir Java modülü (kitaplık veya uygulama) tarafından kullanılan paylaşılan kitaplık bağımlılıklarının yapısı, sınıf yükleyici bağlamı (CLC) olarak bilinir. dexpreopt'un doğruluğunu garanti etmek için derleme zamanı ve çalışma zamanı CLC'leri çakışmalıdır. Derleme zamanı CLC, dex2oat derleyicisinin dexpreopt zamanında kullandığı CLC'dir (ODEX dosyalarına kaydedilir). Çalışma zamanı CLC ise önceden derlenmiş kodun cihaza yüklendiği bağlamdır.

Bu derleme zamanı ve çalışma zamanı CLC'leri hem doğruluk hem de performans açısından aynı olmalıdır. Doğruluk için yinelenen sınıfların işlenmesi gerekir. Çalışma zamanındaki paylaşılan kitaplık bağımlılıkları derleme için kullanılanlardan farklıysa bazı sınıflar farklı şekilde çözümlenebilir ve bu da çalışma zamanında küçük hatalara neden olabilir. Performans, yinelenen sınıflar için çalışma zamanı kontrollerinden de etkilenir.

Etkilenen kullanım alanları

Bu değişikliklerden etkilenen temel kullanım alanı ilk başlatmadır: ART, derleme zamanı ve çalışma zamanı CLC'leri arasında bir uyuşmazlık tespit ederse dexpreopt yapılarını reddeder ve bunun yerine dexopt'u çalıştırır. Uygulamalar arka planda dexopted edilip diske depolanabildiğinden sonraki başlatma işlemleri için bu durum sorun teşkil etmez.

Android'in etkilenen alanları

Bu durum, diğer Java kitaplıklarına çalışma zamanı bağımlılıkları olan tüm Java uygulamalarını ve kitaplıklarını etkiler. Android'de binlerce uygulama vardır ve bunların yüzlercesi paylaşılan kitaplıkları kullanır. İş ortakları da kendi kitaplıkları ve uygulamaları olduğundan bu durumdan etkilenir.

Zarar veren değişiklikler

Derleme sisteminin, dexpreopt derleme kurallarını oluşturmadan önce <uses-library> bağımlılıklarını bilmesi gerekir. Ancak derleme sistemi, derleme kuralları oluştururken (performans nedeniyle) rastgele dosyaları okuyamadığı için manifest dosyasına doğrudan erişip içindeki <uses-library> etiketlerini okuyamaz. Ayrıca manifest, bir APK veya önceden oluşturulmuş bir dosya içinde paketlenmiş olabilir. Bu nedenle, <uses-library> bilgileri derleme dosyalarında (Android.bp veya Android.mk) bulunmalıdır.

ART daha önce, paylaşılan kitaplık bağımlılıklarını (&-classpath olarak bilinir) yoksayan bir geçici çözüm kullanıyordu. Bu geçici çözüm güvenli değildi ve küçük hatalara neden oluyordu. Bu nedenle, Android 12'de kaldırıldı.

Bu nedenle, derleme dosyalarında doğru <uses-library> bilgilerini sağlamayan Java modülleri, derleme hatalarına (derleme zamanı CLC uyuşmazlığından kaynaklanır) veya ilk başlatma süresinde gerilemelere (başlatma zamanı CLC uyuşmazlığından ve ardından dexopt'tan kaynaklanır) neden olabilir.

Taşıma yolu

Bozuk bir derlemeyi düzeltmek için aşağıdaki adımları uygulayın:

  1. Aşağıdaki ayarı yaparak belirli bir ürün için derleme zamanı kontrolünü genel olarak devre dışı bırakın:

    PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true

    ürün makefile'ında. Bu, derleme hatalarını düzeltir (Bozulmaları düzeltme bölümünde listelenen özel durumlar hariç). Ancak bu geçici bir çözümdür ve dexopt'un ardından önyükleme süresinde CLC uyuşmazlığına neden olabilir.

  2. Gerekli <uses-library> bilgilerini derleme dosyalarına ekleyerek derleme zamanı kontrolünü genel olarak devre dışı bırakmadan önce başarısız olan modülleri düzeltin (ayrıntılar için Bozulmaları düzeltme bölümüne bakın). Çoğu modül için bu işlem, Android.bp veya Android.mk'ye birkaç satır eklemeyi gerektirir.

  3. Sorunlu durumlarda modül bazında derleme zamanı kontrolünü ve dexpreopt'u devre dışı bırakın. Önyükleme sırasında reddedilen yapılar için derleme süresini ve depolama alanını boşa harcamamak üzere dexpreopt'u devre dışı bırakın.

  4. 1. adımda ayarlanan PRODUCT_BROKEN_VERIFY_USES_LIBRARIES ayarını kaldırarak derleme zamanı kontrolünü genel olarak yeniden etkinleştirin. Bu değişiklikten sonra derleme başarısız olmamalıdır (2. ve 3. adımlar nedeniyle).

  5. 3. adımda devre dışı bıraktığınız modülleri tek tek düzeltin, ardından dexpreopt ve <uses-library> kontrolünü yeniden etkinleştirin. Gerekirse hataları bildirin.

Android 12'de derleme zamanı <uses-library> kontrolleri zorunlu kılınır.

Bozuklukları düzeltme

Aşağıdaki bölümlerde, belirli bozulma türlerinin nasıl düzeltileceği açıklanmaktadır.

Derleme hatası: CLC uyuşmazlığı

Derleme sistemi, Android.bp veya Android.mk dosyalarındaki bilgiler ile manifest arasında derleme zamanında tutarlılık kontrolü yapar. Derleme sistemi, manifesti okuyamaz ancak manifesti okumak için derleme kuralları oluşturabilir (gerekirse APK'dan çıkararak) ve manifestteki <uses-library> etiketlerini derleme dosyalarındaki <uses-library> bilgileriyle karşılaştırabilir. Kontrol başarısız olursa hata şu şekilde görünür:

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

Hata mesajında da belirtildiği gibi, aciliyet durumuna bağlı olarak birden fazla çözüm vardır:

  • Geçici bir ürün genelinde düzeltme için ürün makefile'ında PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true değerini ayarlayın. Derleme zamanı tutarlılık kontrolü hâlâ yapılır ancak kontrolün başarısız olması derlemenin başarısız olduğu anlamına gelmez. Bunun yerine, kontrol başarısız olduğunda derleme sistemi dex2oat derleyici filtresini dexpreopt'ta verify sürümüne düşürür. Bu da AOT derlemesini bu modül için tamamen devre dışı bırakır.
  • Hızlı ve genel bir komut satırı düzeltmesi için ortam değişkenini RELAX_USES_LIBRARY_CHECK=true kullanın. PRODUCT_BROKEN_VERIFY_USES_LIBRARIES ile aynı etkiye sahiptir ancak komut satırında kullanılmak üzere tasarlanmıştır. Ortam değişkeni, ürün değişkenini geçersiz kılar.
  • Hatayı temel nedenini düzelterek çözmek için derleme sisteminin manifestteki <uses-library> etiketlerinden haberdar olmasını sağlayın. Hata mesajının incelenmesi, soruna hangi kitaplıkların neden olduğunu gösterir (AndroidManifest.xml veya aapt dump badging $APK | grep uses-library ile kontrol edilebilen bir APK'nin içindeki manifest dosyasının incelenmesi de aynı bilgiyi verir).

Android.bp modülleri için:

  1. Eksik kitaplığı modülün libs özelliğinde bulun. Bu tür kitaplıklar varsa Soong, aşağıdaki özel durumlar dışında bunları otomatik olarak ekler:

    • Kitaplık bir SDK kitaplığı değildir (java_sdk_library yerine java_library olarak tanımlanır).
    • Kitaplığın kitaplık adı (manifest dosyasında), modül adından (derleme sisteminde) farklıdır.

    Bu sorunu geçici olarak düzeltmek için provides_uses_lib: "<library-name>" kitaplık tanımına Android.bp ekleyin. Uzun vadeli bir çözüm için temel sorunu düzeltin: Kitaplığı bir SDK kitaplığına dönüştürün veya modülünü yeniden adlandırın.

  2. Önceki adımda çözüm bulunamadıysa gerekli kitaplıklar için uses_libs: ["<library-module-name>"], isteğe bağlı kitaplıklar için ise optional_uses_libs: ["<library-module-name>"] simgesini modülün Android.bp tanımına ekleyin. Bu özellikler, modül adlarının listesini kabul eder. Listedeki kitaplıkların göreli sırası, manifestteki sırayla aynı olmalıdır.

Android.mk modülleri için:

  1. Kitaplığın, modül adından (derleme sisteminde) farklı bir kitaplık adına (manifest dosyasında) sahip olup olmadığını kontrol edin. Bu durumda, kitaplığın Android.mk dosyasında LOCAL_PROVIDES_USES_LIBRARY := <library-name> ekleyerek veya kitaplığın Android.bp dosyasında provides_uses_lib: "<library-name>" ekleyerek bu sorunu geçici olarak düzeltebilirsiniz (Android.mk modülü bir Android.bp kitaplığına bağlı olabileceğinden her iki durum da mümkündür). Uzun vadeli bir çözüm için temel sorunu düzeltin: Kitaplık modülünü yeniden adlandırın.

  2. Gerekli kitaplıklar için LOCAL_USES_LIBRARIES := <library-module-name>, isteğe bağlı kitaplıklar için LOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name> öğesini modülün Android.mk tanımına ekleyin. Bu özellikler, modül adlarının listesini kabul eder. Listedeki kitaplıkların göreli sırası, manifest dosyasındakiyle aynı olmalıdır.

Derleme hatası: bilinmeyen kitaplık yolu

Derleme sistemi, <uses-library> DEX jar'ı için bir yol bulamazsa (ana makinede derleme zamanı yolu veya cihazda yükleme yolu) genellikle derleme başarısız olur. Bir yolun bulunamaması, kitaplığın beklenmedik bir şekilde yapılandırıldığını gösterebilir. Sorunlu modül için dexpreopt'u devre dışı bırakarak derlemeyi geçici olarak düzeltin.

Android.bp (modül özellikleri):

enforce_uses_libs: false,
dex_preopt: {
    enabled: false,
},

Android.mk (modül değişkenleri):

LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false

Desteklenmeyen senaryoları araştırmak için hata kaydı oluşturun.

Derleme hatası: Kitaplık bağımlılığı eksik

Y modülünün manifest dosyasından Y'nin derleme dosyasına <uses-library> X ekleme girişimi, eksik bağımlılık X nedeniyle derleme hatasıyla sonuçlanabilir.

Bu, Android.bp modülleri için örnek bir hata mesajıdır:

"Y" depends on undefined module "X"

Bu, Android.mk modülleri için örnek bir hata mesajıdır:

'.../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

Bu tür hataların yaygın bir kaynağı, bir kitaplığın, derleme sisteminde karşılık gelen modülünden farklı şekilde adlandırılmasıdır. Örneğin, manifest <uses-library> girişi com.android.X ise ancak kitaplık modülünün adı yalnızca X ise bu durum hataya neden olur. Bu durumu çözmek için derleme sistemine, X adlı modülün com.android.X adlı <uses-library> sağladığını söyleyin.

Bu, Android.bp kitaplıkları (modül özelliği) için bir örnektir:

provides_uses_lib: “com.android.X”,

Bu, Android.mk kitaplıkları (modül değişkeni) için bir örnektir:

LOCAL_PROVIDES_USES_LIBRARY := com.android.X

Önyükleme zamanı CLC uyuşmazlığı

İlk başlatmada, aşağıda gösterildiği gibi CLC uyuşmazlığıyla ilgili mesajlar için logcat'i arayın:

$ adb wait-for-device && adb logcat \
  | grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1

Çıkışta, burada gösterilen biçimde mesajlar olabilir:

[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...

CLC uyuşmazlığı uyarısı alırsanız hatalı modül için bir dexopt komutu arayın. Bu sorunu düzeltmek için modülün derleme zamanı kontrolünün başarılı olduğundan emin olun. Bu işe yaramazsa sizinki, derleme sistemi tarafından desteklenmeyen özel bir durum olabilir (ör. bir kitaplığı değil, başka bir APK'yı yükleyen bir uygulama). Derleme sırasında uygulamanın çalışma zamanında ne yüklediğini kesin olarak bilmek mümkün olmadığından derleme sistemi tüm durumları ele almaz.

Sınıf yükleyici bağlamı

CLC, sınıf yükleyici hiyerarşisini açıklayan ağaç benzeri bir yapıdır. Derleme sistemi, CLC'yi dar anlamda kullanır (yalnızca kitaplıkları kapsar, APK'ları veya özel sınıf yükleyicileri kapsamaz): Bir kitaplığın ya da uygulamanın tüm <uses-library> bağımlılıklarının geçişli kapanımını temsil eden bir kitaplık ağacıdır. CLC'nin en üst düzey öğeleri, manifestte (sınıf yolu) belirtilen doğrudan <uses-library> bağımlılıklarıdır. Bir CLC ağacının her düğümü, kendi <uses-library> alt düğümlerine sahip olabilen bir <uses-library> düğümüdür.

<uses-library> bağımlılıkları yönlendirilmiş döngüsüz bir grafik olduğundan ve mutlaka bir ağaç olmadığından CLC, aynı kitaplık için birden fazla alt ağaç içerebilir. Diğer bir deyişle, CLC, ağaç şeklinde "açılmış" bağımlılık grafiğidir. Yalnızca mantıksal düzeyde çoğaltma yapılır. Temel sınıf yükleyiciler çoğaltılmaz (çalışma zamanında her kitaplık için tek bir sınıf yükleyici örneği vardır).

CLC, kitaplık veya uygulama tarafından kullanılan Java sınıflarını çözerken kitaplıkların arama sırasını tanımlar. Kitaplıklar yinelenen sınıflar içerebileceği ve sınıf ilk eşleşmeye göre çözüldüğü için arama sırası önemlidir.

Cihaz üzerinde (çalışma zamanı) CLC

PackageManager (frameworks/base içinde), cihazda bir Java modülünü yüklemek için CLC oluşturur. Kitaplıklar, modülün manifest dosyasındaki <uses-library> etiketlerinde listelenen kitaplıkları üst düzey CLC öğeleri olarak ekler.

Kullanılan her kitaplık için PackageManager, tüm <uses-library> bağımlılıklarını (kitaplığın manifest dosyasında etiket olarak belirtilir) alır ve her bağımlılık için iç içe yerleştirilmiş bir CLC ekler. Bu işlem, oluşturulan CLC ağacının tüm yaprak düğümleri <uses-library> bağımlılıkları olmayan kitaplıklar olana kadar yinelemeli olarak devam eder.

PackageManager yalnızca paylaşılan kitaplıkları bilir. Bu kullanımda paylaşılanın tanımı, normal anlamından (paylaşılan ve statik gibi) farklıdır. Android'de, cihazda yüklü XML yapılandırmalarında listelenen Java paylaşılan kitaplıklarıdır (/system/etc/permissions/platform.xml). Her giriş, paylaşılan bir kitaplığın adını, DEX jar dosyasının yolunu ve bağımlılıkların listesini (bu kitaplığın çalışma zamanında kullandığı diğer paylaşılan kitaplıklar ve manifest dosyasındaki <uses-library> etiketlerinde belirtilenler) içerir.

Diğer bir deyişle, çalışma zamanında CLC'nin oluşturulmasına olanak tanıyan iki bilgi kaynağı vardır: manifest'teki PackageManager <uses-library> etiketleri ve XML yapılandırmalarındaki paylaşılan kitaplık bağımlılıkları.

Ana makinede (derleme zamanı) CLC

CLC yalnızca bir kitaplık veya uygulama yüklenirken değil, derlenirken de gereklidir. Derleme, cihazda (dexopt) veya derleme sırasında (dexpreopt) gerçekleşebilir. dexopt cihaz üzerinde gerçekleştiğinden PackageManager (manifestler ve paylaşılan kitaplık bağımlılıkları) ile aynı bilgilere sahiptir. Ancak Dexpreopt, ana makinede ve tamamen farklı bir ortamda gerçekleşir ve derleme sisteminden aynı bilgileri alması gerekir.

Bu nedenle, dexpreopt tarafından kullanılan derleme zamanı CLC ile PackageManager tarafından kullanılan çalışma zamanı CLC aynı şeydir ancak iki farklı şekilde hesaplanır.

Derleme zamanı ve çalışma zamanı CLC'leri çakışmalıdır. Aksi takdirde, dexpreopt tarafından oluşturulan AOT ile derlenmiş kod reddedilir. Derleme zamanı ve çalışma zamanı CLC'lerinin eşitliğini kontrol etmek için dex2oat derleyicisi, derleme zamanı CLC'yi *.odex dosyalarına (OAT dosyası başlığının classpath alanında) kaydeder. Depolanan CLC'yi bulmak için şu komutu kullanın:

oatdump --oat-file=<FILE> | grep '^classpath = '

Önyükleme sırasında logcat'te derleme zamanı ve çalışma zamanı CLC uyuşmazlığı bildirilir. Aşağıdaki komutu kullanarak arayın:

logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'

Uyuşmazlık, kitaplığın veya uygulamanın ya dexopted olması ya da optimizasyon olmadan çalışması gerektiğinden (örneğin, uygulamanın kodunun APK'dan belleğe çıkarılması gerekebilir ki bu çok maliyetli bir işlemdir) performans açısından kötüdür.

Paylaşılan kitaplık isteğe bağlı veya zorunlu olabilir. dexpreopt açısından, gerekli bir kitaplık derleme sırasında mevcut olmalıdır (yokluğu derleme hatasıdır). İsteğe bağlı bir kitaplık, derleme sırasında mevcut olabilir veya olmayabilir. Mevcutsa CLC'ye eklenir, dex2oat'a iletilir ve *.odex dosyasına kaydedilir. İsteğe bağlı bir kitaplık yoksa atlanır ve CLC'ye eklenmez. Derleme zamanı ve çalışma zamanı durumu arasında bir uyuşmazlık varsa (isteğe bağlı kitaplık bir durumda mevcutken diğerinde mevcut değilse) derleme zamanı ve çalışma zamanı CLC'leri eşleşmez ve derlenmiş kod reddedilir.

Gelişmiş derleme sistemi ayrıntıları (manifest düzeltici)

Bazen bir kitaplığın veya uygulamanın kaynak manifest dosyasında <uses-library> etiketleri eksik olabilir. Örneğin, kitaplığın veya uygulamanın geçişli bağımlılıklarından biri başka bir <uses-library> etiketi kullanmaya başlarsa ve kitaplığın veya uygulamanın manifest dosyası bu etiketi içerecek şekilde güncellenmezse bu durum oluşabilir.

Soong, belirli bir kitaplık veya uygulama için eksik <uses-library> etiketlerinden bazılarını otomatik olarak hesaplayabilir. Bunun nedeni, kitaplığın veya uygulamanın geçişli bağımlılık kapatımındaki SDK kitaplıklarıdır. Kitaplık (veya uygulama), bir SDK kitaplığına bağımlı olan bir statik kitaplığa bağımlı olabileceği ve muhtemelen başka bir kitaplık aracılığıyla tekrar geçişli olarak bağımlı olabileceği için kapatım gereklidir.

Tüm <uses-library> etiketleri bu şekilde hesaplanamaz ancak mümkün olduğunda Soong'un manifest girişlerini otomatik olarak eklemesine izin vermek tercih edilir. Bu yöntem, daha az hataya neden olur ve bakımı kolaylaştırır. Örneğin, birçok uygulama yeni bir <uses-library> bağımlılığı ekleyen statik bir kitaplık kullandığında tüm uygulamaların güncellenmesi gerekir. Bu da sürdürmesi zor bir durumdur.