Dexpreopt و <uses-library> چک ها

اندروید 12 دارای تغییرات سیستمی برای کامپایل AOT از فایل های DEX (dexpreopt) برای ماژول های جاوا است که وابستگی های <uses-library> دارند. در برخی موارد، این تغییرات سیستم ساخت می‌تواند ساخت‌ها را خراب کند. از این صفحه برای آماده شدن برای شکستگی ها استفاده کنید و دستور العمل های موجود در این صفحه را برای رفع و کاهش آنها دنبال کنید.

Dexpreopt فرآیند جمع‌آوری پیش از موعد کتابخانه‌ها و برنامه‌های جاوا است. Dexpreopt در زمان ساخت روی هاست اتفاق می افتد (برخلاف دکسوپت که روی دستگاه اتفاق می افتد). ساختار وابستگی های کتابخانه مشترک که توسط یک ماژول جاوا (یک کتابخانه یا یک برنامه) استفاده می شود، به عنوان زمینه بارگذار کلاس (CLC) شناخته می شود. برای تضمین صحت dexpreopt، CLC های زمان ساخت و زمان اجرا باید مطابقت داشته باشند. Build-time CLC چیزی است که کامپایلر dex2oat در زمان dexpreopt استفاده می‌کند (در فایل‌های ODEX ثبت می‌شود)، و CLC زمان اجرا، زمینه‌ای است که در آن کد از پیش کامپایل شده روی دستگاه بارگذاری می‌شود.

این CLC های زمان ساخت و زمان اجرا باید به دلایل صحت و عملکرد مطابقت داشته باشند. برای صحت، لازم است که کلاس های تکراری را مدیریت کنید. اگر وابستگی‌های کتابخانه مشترک در زمان اجرا با وابستگی‌های مورد استفاده برای کامپایل متفاوت باشد، ممکن است برخی از کلاس‌ها به روشی متفاوت حل شوند و باعث ایجاد باگ‌های ظریف در زمان اجرا شوند. عملکرد همچنین تحت تأثیر بررسی های زمان اجرا برای کلاس های تکراری قرار می گیرد.

موارد استفاده تحت تأثیر

اولین بوت مورد استفاده اصلی است که تحت تأثیر این تغییرات قرار می گیرد: اگر ART عدم تطابق بین CLC های زمان ساخت و زمان اجرا را تشخیص دهد، آرتیفکت های dexpreopt را رد می کند و به جای آن dexopt را اجرا می کند. برای بوت های بعدی این کار خوب است زیرا برنامه ها را می توان در پس زمینه حذف کرد و روی دیسک ذخیره کرد.

مناطق آسیب دیده اندروید

این روی همه برنامه‌ها و کتابخانه‌های جاوا که وابستگی زمان اجرا به سایر کتابخانه‌های جاوا دارند، تأثیر می‌گذارد. اندروید هزاران برنامه دارد و صدها مورد از آنها از کتابخانه های مشترک استفاده می کنند. شرکا نیز تحت تأثیر قرار می گیرند، زیرا آنها کتابخانه ها و برنامه های خود را دارند.

تغییرات را بشکنید

سیستم ساخت قبل از ایجاد قوانین ساخت dexpreopt باید وابستگی های <uses-library> را بداند. با این حال، نمی‌تواند مستقیماً به مانیفست دسترسی داشته باشد و تگ‌های <uses-library> را در آن بخواند، زیرا سیستم ساخت اجازه ندارد فایل‌های دلخواه را هنگام ایجاد قوانین ساخت (به دلایل عملکرد) بخواند. علاوه بر این، مانیفست ممکن است در داخل یک APK یا یک پیش ساخته بسته بندی شود. بنابراین، اطلاعات <uses-library> باید در فایل های ساخت ( Android.bp یا Android.mk ) وجود داشته باشد.

قبلاً ART از راه‌حلی استفاده می‌کرد که وابستگی‌های کتابخانه مشترک (معروف به &-classpath ) را نادیده می‌گرفت. این ناامن بود و باگ‌های ظریفی را ایجاد می‌کرد، بنابراین راه‌حل در Android 12 حذف شد.

در نتیجه، ماژول‌های جاوا که اطلاعات <uses-library> صحیح را در فایل‌های ساخت خود ارائه نمی‌کنند، می‌توانند باعث خرابی ساخت (ناشی از عدم تطابق CLC در زمان ساخت) یا رگرسیون زمان اولین راه‌اندازی (ناشی از CLC در زمان راه‌اندازی) شوند. عدم تطابق و به دنبال آن دکسوپت).

مسیر مهاجرت

مراحل زیر را برای تعمیر یک ساختمان شکسته دنبال کنید:

  1. با تنظیم، بررسی زمان ساخت را برای یک محصول خاص غیرفعال کنید

    PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true

    در فایل ساخت محصول این کار خطاهای ساخت (به جز موارد خاص که در بخش Fixing breakages ذکر شده است) را برطرف می کند. با این حال، این یک راه‌حل موقت است و می‌تواند باعث عدم تطابق CLC در زمان راه‌اندازی و سپس dexopt شود.

  2. با افزودن اطلاعات <uses-library> لازم به فایل‌های ساخت آن‌ها، ماژول‌هایی را که قبل از غیرفعال کردن سراسری بررسی زمان ساخت شکست خورده‌اند، برطرف کنید (برای جزئیات به رفع شکستگی‌ها مراجعه کنید). برای اکثر ماژول ها این نیاز به افزودن چند خط در Android.bp یا Android.mk دارد.

  3. بررسی زمان ساخت را غیرفعال کنید و موارد مشکل‌ساز را بر اساس هر ماژول بازپس بگیرید. dexpreopt را غیرفعال کنید تا زمان ساخت و ذخیره سازی را برای مصنوعاتی که در هنگام بوت رد می شوند هدر ندهید.

  4. با لغو تنظیمات PRODUCT_BROKEN_VERIFY_USES_LIBRARIES که در مرحله 1 تنظیم شده بود، بررسی زمان ساخت را مجدداً فعال کنید. ساخت نباید بعد از این تغییر (به دلیل مراحل 2 و 3) شکست بخورد.

  5. ماژول هایی را که در مرحله 3 غیرفعال کرده اید، یکی یکی برطرف کنید، سپس dexpreopt و تیک <uses-library> را دوباره فعال کنید. در صورت لزوم باگ ها را پر کنید.

بررسی‌های <uses-library> در زمان ساخت در Android 12 اعمال می‌شوند.

شکستگی ها را برطرف کنید

بخش های زیر به شما می گویند که چگونه انواع خاصی از شکستگی را برطرف کنید.

خطای ساخت: عدم تطابق CLC

سیستم ساخت یک بررسی هماهنگی زمان ساخت بین اطلاعات موجود در فایل‌های Android.bp یا Android.mk و مانیفست انجام می‌دهد. سیستم ساخت نمی‌تواند مانیفست را بخواند، اما می‌تواند قوانین ساخت را برای خواندن مانیفست ایجاد کند (در صورت لزوم آن را از APK استخراج می‌کند)، و تگ‌های <uses-library> را در مانیفست با اطلاعات <uses-library> در مانیفست مقایسه می‌کند. فایل های ساخت اگر بررسی ناموفق باشد، خطا به صورت زیر است:

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

همانطور که پیام خطا نشان می دهد، بسته به فوریت، چندین راه حل وجود دارد:

  • برای رفع موقت در سراسر محصول ، PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true در فایل ساخت محصول تنظیم کنید. بررسی انسجام زمان ساخت هنوز انجام می شود، اما شکست چک به معنای شکست ساخت نیست. در عوض، خرابی بررسی باعث می‌شود که سیستم ساخت، فیلتر کامپایلر dex2oat را برای verify در dexpreopt کاهش دهد، که کامپایل AOT را به طور کامل برای این ماژول غیرفعال می‌کند.
  • برای رفع سریع و کلی خط فرمان ، از متغیر محیطی RELAX_USES_LIBRARY_CHECK=true استفاده کنید. اثری مشابه PRODUCT_BROKEN_VERIFY_USES_LIBRARIES دارد، اما برای استفاده در خط فرمان در نظر گرفته شده است. متغیر محیطی بر متغیر محصول غلبه می کند.
  • برای راه‌حلی برای رفع خطا، سیستم ساخت را از تگ‌های <uses-library> در مانیفست آگاه کنید. بازرسی پیام خطا نشان می‌دهد که کدام کتابخانه‌ها مشکل را ایجاد می‌کنند (همانطور که بازرسی AndroidManifest.xml یا مانیفست داخل یک APK که می‌تواند با « aapt dump badging $APK | grep uses-library » بررسی شود.

برای ماژول های Android.bp :

  1. به دنبال کتابخانه گم شده در ویژگی libs ماژول بگردید. اگر وجود دارد، Soong معمولاً چنین کتابخانه‌هایی را به‌طور خودکار اضافه می‌کند، مگر در موارد خاص:

    • این کتابخانه یک کتابخانه SDK نیست (به جای java_sdk_library به عنوان java_library تعریف شده است).
    • کتابخانه نام کتابخانه متفاوتی (در مانیفست) با نام ماژول خود (در سیستم ساخت) دارد.

    برای رفع موقت این مشکل، provides_uses_lib: "<library-name>" در تعریف کتابخانه Android.bp اضافه کنید. برای یک راه حل طولانی مدت، مشکل اساسی را برطرف کنید: کتابخانه را به کتابخانه SDK تبدیل کنید یا ماژول آن را تغییر نام دهید.

  2. اگر مرحله قبل وضوحی ارائه نکرد، uses_libs: ["<library-module-name>"] برای کتابخانه های مورد نیاز، یا optional_uses_libs: ["<library-module-name>"] برای کتابخانه های اختیاری به Android.bp تعریف ماژول. این ویژگی ها لیستی از نام ماژول ها را می پذیرند. ترتیب نسبی کتابخانه‌های موجود در فهرست باید مانند ترتیب در مانیفست باشد.

برای ماژول های Android.mk :

  1. بررسی کنید که آیا کتابخانه نام کتابخانه متفاوتی (در مانیفست) با نام ماژول آن (در سیستم ساخت) دارد یا خیر. اگر این کار را کرد، با افزودن LOCAL_PROVIDES_USES_LIBRARY := <library-name> در فایل Android.mk کتابخانه، موقتاً این مشکل را برطرف کنید، یا provides_uses_lib: "<library-name>" در فایل Android.bp کتابخانه (هر دو مورد) این امکان وجود دارد زیرا یک ماژول Android.mk ممکن است به کتابخانه Android.bp بستگی داشته باشد. برای یک راه حل طولانی مدت، مشکل اساسی را برطرف کنید: نام ماژول کتابخانه را تغییر دهید.

  2. LOCAL_USES_LIBRARIES := <library-module-name> برای کتابخانه های مورد نیاز اضافه کنید. LOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name> برای کتابخانه های اختیاری به تعریف Android.mk ماژول اضافه کنید. این ویژگی ها لیستی از نام ماژول ها را می پذیرند. ترتیب نسبی کتابخانه ها در لیست باید مانند مانیفست باشد.

خطای ساخت: مسیر کتابخانه ناشناخته

اگر سیستم ساخت نتواند مسیری برای یک <uses-library> jar DEX پیدا کند (یا مسیر زمان ساخت در میزبان یا مسیر نصب روی دستگاه)، معمولاً ساخت را با شکست مواجه می‌کند. عدم یافتن مسیر می تواند نشان دهنده پیکربندی کتابخانه به روشی غیرمنتظره باشد. با غیرفعال کردن dexpreopt برای ماژول مشکل ساز، ساخت را به طور موقت اصلاح کنید.

Android.bp (ویژگی های ماژول):

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

Android.mk (متغیرهای ماژول):

LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false

برای بررسی سناریوهای پشتیبانی نشده، یک اشکال را ثبت کنید.

خطای ساخت: وابستگی کتابخانه وجود ندارد

تلاش برای افزودن <uses-library> X از مانیفست ماژول Y به فایل ساخت Y ممکن است به دلیل عدم وابستگی X منجر به خطای ساخت شود.

این یک پیام خطای نمونه برای ماژول های Android.bp است:

"Y" depends on undefined module "X"

این یک پیام خطای نمونه برای ماژول های 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

یک منبع رایج چنین خطاهایی زمانی است که نام یک کتابخانه متفاوت از نام ماژول مربوطه آن در سیستم ساخت است. برای مثال، اگر ورودی مانیفست <uses-library> com.android.X باشد، اما نام ماژول کتابخانه فقط X باشد، باعث خطا می شود. برای حل این مورد، به سیستم ساخت بگویید که ماژول به نام X یک <uses-library> به نام com.android.X را ارائه می دهد.

این یک مثال برای کتابخانه های Android.bp (ویژگی ماژول) است:

provides_uses_lib: “com.android.X”,

این یک مثال برای کتابخانه های Android.mk (متغیر ماژول) است:

LOCAL_PROVIDES_USES_LIBRARY := com.android.X

عدم تطابق CLC در زمان راه اندازی

در ابتدا، logcat را برای پیام های مربوط به عدم تطابق CLC جستجو کنید، همانطور که در زیر نشان داده شده است:

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

خروجی می تواند دارای پیام هایی به شکلی باشد که در اینجا نشان داده شده است:

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

اگر هشدار عدم تطابق CLC دریافت کردید، به دنبال دستور dexopt برای ماژول معیوب باشید. برای رفع آن، مطمئن شوید که بررسی زمان ساخت ماژول انجام می شود. اگر کار نکرد، ممکن است مورد شما یک مورد خاص باشد که توسط سیستم ساخت پشتیبانی نمی‌شود (مانند برنامه‌ای که APK دیگر را بارگیری می‌کند، نه کتابخانه). سیستم ساخت تمام موارد را مدیریت نمی کند، زیرا در زمان ساخت نمی توان به طور قطعی دانست که برنامه در زمان اجرا چه چیزی بارگذاری می شود.

زمینه بارگذار کلاس

CLC یک ساختار درخت مانند است که سلسله مراتب کلاس-لودر را توصیف می کند. سیستم ساخت از CLC به معنای محدود استفاده می‌کند (فقط کتابخانه‌ها را پوشش می‌دهد، نه APK یا بارکننده‌های کلاس سفارشی): این درختی از کتابخانه‌ها است که بسته شدن موقت همه وابستگی‌های <uses-library> یک کتابخانه یا برنامه را نشان می‌دهد. عناصر سطح بالای یک CLC وابستگی های مستقیم <uses-library> هستند که در مانیفست (مسیر کلاس) مشخص شده اند. هر گره درخت CLC یک گره <uses-library> است که ممکن است زیرگره های <uses-library> خود را داشته باشد.

از آنجا که وابستگی‌های <uses-library> یک گراف غیر چرخه‌ای جهت‌دار هستند و لزوماً یک درخت نیستند، CLC می‌تواند شامل چندین زیردرخت برای یک کتابخانه باشد. به عبارت دیگر، CLC نمودار وابستگی است که به یک درخت "باز شده" است. تکرار فقط در سطح منطقی است. بارگذارهای کلاس اصلی واقعی تکراری نیستند (در زمان اجرا یک نمونه بارکننده کلاس برای هر کتابخانه وجود دارد).

CLC ترتیب جستجوی کتابخانه ها را هنگام حل کلاس های جاوا که توسط کتابخانه یا برنامه استفاده می شود را تعیین می کند. ترتیب جستجو مهم است زیرا کتابخانه ها می توانند دارای کلاس های تکراری باشند و کلاس به اولین تطابق حل می شود.

روی دستگاه (زمان اجرا) CLC

PackageManager (در frameworks/base ) یک CLC برای بارگذاری یک ماژول جاوا بر روی دستگاه ایجاد می کند. کتابخانه های فهرست شده در تگ های <uses-library> در مانیفست ماژول را به عنوان عناصر CLC سطح بالا اضافه می کند.

برای هر کتابخانه استفاده شده، PackageManager تمام وابستگی های <uses-library> خود را دریافت می کند (که به عنوان برچسب در مانیفست آن کتابخانه مشخص شده است) و یک CLC تودرتو برای هر وابستگی اضافه می کند. این فرآیند به صورت بازگشتی ادامه می‌یابد تا زمانی که تمام گره‌های برگ درخت CLC ساخته شده، کتابخانه‌هایی بدون وابستگی <uses-library> باشند.

PackageManager فقط از کتابخانه های مشترک آگاه است. تعریف اشتراک‌گذاری در این کاربرد با معنای معمول آن (مانند اشتراک‌گذاری در مقابل استاتیک) متفاوت است. در Android، کتابخانه‌های مشترک جاوا آنهایی هستند که در پیکربندی‌های XML فهرست شده‌اند که روی دستگاه نصب می‌شوند ( /system/etc/permissions/platform.xml ). هر ورودی حاوی نام یک کتابخانه مشترک، مسیری به فایل jar DEX آن، و لیستی از وابستگی ها است (دیگر کتابخانه های مشترکی که این یکی در زمان اجرا استفاده می کند، و در تگ های <uses-library> در مانیفست خود مشخص می کند).

به عبارت دیگر، دو منبع اطلاعاتی وجود دارد که به PackageManager اجازه می‌دهد تا CLC را در زمان اجرا بسازد: تگ‌های <uses-library> در مانیفست، و وابستگی‌های کتابخانه مشترک در تنظیمات XML.

CLC روی میزبان (زمان ساخت).

CLC نه تنها هنگام بارگذاری یک کتابخانه یا یک برنامه مورد نیاز است، بلکه در هنگام کامپایل نیز مورد نیاز است. کامپایل می تواند روی دستگاه (dexopt) یا در حین ساخت (dexpreopt) اتفاق بیفتد. از آنجایی که dexopt روی دستگاه انجام می‌شود، اطلاعاتی مشابه با PackageManager (وابستگی‌های کتابخانه‌ای آشکار و مشترک) دارد. با این حال، Dexpreopt روی هاست و در محیطی کاملاً متفاوت انجام می شود و باید همان اطلاعات را از سیستم ساخت دریافت کند.

بنابراین، CLC زمان ساخت که توسط dexpreopt استفاده می‌شود و CLC زمان اجرا که توسط PackageManager استفاده می‌شود یکسان هستند، اما به دو روش متفاوت محاسبه می‌شوند.

CLC های زمان ساخت و زمان اجرا باید مطابق باشند، در غیر این صورت کد کامپایل شده توسط AOT ایجاد شده توسط dexpreopt رد می شود. برای بررسی برابری CLC های زمان ساخت و زمان اجرا، کامپایلر dex2oat CLC زمان ساخت را در فایل های *.odex (در قسمت classpath سربرگ فایل OAT) ثبت می کند. برای پیدا کردن CLC ذخیره شده، از این دستور استفاده کنید:

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

عدم تطابق CLC زمان ساخت و زمان اجرا در logcat در هنگام بوت گزارش می شود. با این دستور آن را جستجو کنید:

logcat | grep -E 'ClassLoaderContext [az ]+ mismatch'

عدم تطابق برای عملکرد بد است، زیرا کتابخانه یا برنامه را مجبور می‌کند که حذف شود یا بدون بهینه‌سازی اجرا شود (برای مثال، ممکن است نیاز باشد کد برنامه در حافظه از APK استخراج شود، یک عملیات بسیار گران‌قیمت).

یک کتابخانه مشترک می تواند اختیاری یا ضروری باشد. از نقطه نظر dexpreopt، یک کتابخانه مورد نیاز باید در زمان ساخت وجود داشته باشد (عدم وجود آن یک خطای ساخت است). یک کتابخانه اختیاری می تواند در زمان ساخت وجود داشته باشد یا وجود نداشته باشد: در صورت وجود، به CLC اضافه می شود، به dex2oat منتقل می شود و در فایل *.odex ثبت می شود. اگر یک کتابخانه اختیاری وجود نداشته باشد، از آن صرفنظر می شود و به CLC اضافه نمی شود. اگر بین وضعیت زمان ساخت و زمان اجرا عدم تطابق وجود داشته باشد (کتابخانه اختیاری در یک مورد وجود دارد، اما در مورد دیگر وجود ندارد)، CLC های زمان ساخت و زمان اجرا مطابقت ندارند و کد کامپایل شده رد می شود.

جزئیات سیستم ساخت پیشرفته (تعیین کننده مانیفست)

گاهی اوقات تگ های <uses-library> در مانیفست منبع کتابخانه یا برنامه وجود ندارد. برای مثال، اگر یکی از وابستگی‌های گذرای کتابخانه یا برنامه شروع به استفاده از تگ <uses-library> دیگری کند و مانیفست کتابخانه یا برنامه برای گنجاندن آن به‌روزرسانی نشود، این اتفاق می‌افتد.

Soong می‌تواند برخی از برچسب‌های <uses-library> گمشده را برای یک کتابخانه یا برنامه به طور خودکار محاسبه کند، همانطور که کتابخانه‌های SDK در بسته شدن وابستگی انتقالی کتابخانه یا برنامه. بسته شدن لازم است زیرا ممکن است کتابخانه (یا برنامه) به کتابخانه ایستا وابسته به کتابخانه SDK بستگی داشته باشد، و احتمالاً ممکن است دوباره به صورت گذرا از طریق کتابخانه دیگری وابسته باشد.

همه تگ‌های <uses-library> را نمی‌توان با این روش محاسبه کرد، اما در صورت امکان، ترجیح داده می‌شود به Soong اجازه دهید ورودی‌های مانیفست را به صورت خودکار اضافه کند. کمتر مستعد خطا است و تعمیر و نگهداری را ساده می کند. به عنوان مثال، هنگامی که بسیاری از برنامه‌ها از یک کتابخانه ثابت استفاده می‌کنند که یک وابستگی جدید <uses-library> اضافه می‌کند، همه برنامه‌ها باید به‌روزرسانی شوند، که نگهداری آن دشوار است.