يتضمّن نظام التشغيل Android 12 تغييرات في نظام التصميم على عملية الترجمة المسبقة (AOT) لملفات DEX (dexpreopt) الخاصة بوحدات Java التي تتضمّن تبعيات <uses-library>. في بعض الحالات، يمكن أن تؤدي هذه التغييرات في نظام الإنشاء إلى تعطيل عمليات الإنشاء. استخدِم هذه الصفحة للاستعداد لحدوث أخطاء، واتّبِع الوصفات الواردة في هذه الصفحة لإصلاحها والحدّ منها.
Dexpreopt هي عملية تجميع مسبق لمكتبات وتطبيقات Java. تتم عملية Dexpreopt على المضيف في مدّة التصميم (على عكس dexopt التي تتم على الجهاز فقط). يُعرف هيكل العناصر الاعتمادية للمكتبة المشتركة التي يستخدمها أحد وحدات Java (مكتبة أو تطبيق) باسم سياق أداة تحميل الفئات (CLC). لضمان صحة dexpreopt، يجب أن تتطابق رموز CLC في وقت الإنشاء ووقت التشغيل. إنّ CLC في وقت الإنشاء هو ما يستخدمه برنامج dex2oat المترجم في وقت dexpreopt (ويتم تسجيله في ملفات ODEX)، أما CLC في وقت التشغيل فهو السياق الذي يتم فيه تحميل الرمز البرمجي المترجم مسبقًا على الجهاز.
يجب أن تتطابق رموز CLC في وقت الإنشاء ووقت التشغيل لأسباب تتعلّق بكل من الصحة والأداء. لضمان صحة البيانات، من الضروري التعامل مع الفئات المكرّرة. إذا كانت التبعيات الخاصة بالمكتبة المشترَكة في وقت التشغيل مختلفة عن تلك المستخدَمة في عملية التجميع، قد يتم تحديد بعض الفئات بشكل مختلف، ما يؤدي إلى حدوث أخطاء طفيفة في وقت التشغيل. يتأثر الأداء أيضًا بعمليات التحقّق أثناء وقت التشغيل من الفئات المكرّرة.
حالات الاستخدام المتأثرة
إنّ عملية التشغيل الأولى هي حالة الاستخدام الرئيسية التي تتأثر بهذه التغييرات: إذا رصدت ART عدم تطابق بين رموز CLC في وقت الإنشاء ووقت التشغيل، سترفض عناصر dexpreopt وتنفّذ dexopt بدلاً من ذلك. ويكون ذلك مقبولاً في عمليات التشغيل اللاحقة لأنّه يمكن إجراء عملية dexopt للتطبيقات في الخلفية وتخزينها على القرص.
المناطق المتأثّرة في Android
ويؤثر ذلك في جميع تطبيقات Java ومكتباتها التي تعتمد على مكتبات Java أخرى أثناء التشغيل. يتضمّن Android آلاف التطبيقات، ويستخدم المئات منها المكتبات المشتركة. ويتأثر الشركاء أيضًا لأنّ لديهم مكتبات وتطبيقات خاصة بهم.
تغييرات قد تؤدي إلى أعطال
يحتاج نظام التصميم إلى معرفة تبعيات <uses-library> قبل إنشاء قواعد الإنشاء الخاصة بـ dexpreopt. ومع ذلك، لا يمكنه الوصول إلى ملف البيان مباشرةً وقراءة علامات <uses-library> فيه، لأنّه لا يُسمح لنظام التصميم بقراءة ملفات عشوائية عند إنشاء قواعد التصميم (لأسباب تتعلق بالأداء). علاوةً على ذلك، قد يتم تضمين البيان داخل حزمة APK أو حزمة مُنشأة مسبقًا. لذلك، يجب أن تتوفّر <uses-library>
المعلومات في ملفات الإصدار (Android.bp أو Android.mk).
في السابق، كان ART يستخدم حلاً بديلاً يتجاهل تبعيات المكتبة المشتركة (المعروفة باسم &-classpath). كان هذا الحل غير آمن ويتسبّب في حدوث أخطاء طفيفة، لذا تمت إزالته في نظام التشغيل Android 12.
نتيجةً لذلك، يمكن أن تتسبّب وحدات Java التي لا تقدّم معلومات <uses-library>
صحيحة في ملفات الإنشاء في حدوث أعطال في الإنشاء (بسبب عدم تطابق CLC في وقت الإنشاء) أو تراجع في وقت التشغيل الأول (بسبب عدم تطابق CLC في وقت التشغيل متبوعًا بعملية dexopt).
مسار نقل البيانات
اتّبِع الخطوات التالية لإصلاح إصدار معطّل:
إيقاف عملية التحقّق في وقت الإنشاء لمنتج معيّن على مستوى العالم من خلال ضبط
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := trueفي ملف makefile الخاص بالمنتج يؤدي ذلك إلى إصلاح أخطاء الإنشاء (باستثناء الحالات الخاصة المذكورة في قسم إصلاح المشاكل). ومع ذلك، هذا حل مؤقت، ويمكن أن يؤدي إلى عدم تطابق CLC في وقت التشغيل ثم dexopt.
أصلِح الوحدات التي تعذّر التحقّق منها قبل إيقاف عملية التحقّق أثناء وقت الإنشاء على مستوى العالم عن طريق إضافة معلومات
<uses-library>اللازمة إلى ملفات الإنشاء الخاصة بها (راجِع إصلاح المشاكل للحصول على التفاصيل). بالنسبة إلى معظم الوحدات، يتطلّب ذلك إضافة بضعة أسطر فيAndroid.bpأو فيAndroid.mk.إيقاف عملية التحقّق في وقت الإنشاء وdexpreopt للحالات التي تتضمّن مشاكل، على مستوى كل وحدة. عطِّل dexpreopt حتى لا تضيّع وقت الإنشاء ومساحة التخزين على العناصر التي يتم رفضها عند بدء التشغيل.
أعِد تفعيل عملية التحقّق أثناء الإنشاء على مستوى العالم من خلال إلغاء ضبط
PRODUCT_BROKEN_VERIFY_USES_LIBRARIESالذي تم ضبطه في الخطوة 1، ولن يتعذّر الإنشاء بعد هذا التغيير (بسبب الخطوتين 2 و3).أصلِح الوحدات التي أوقفتها في الخطوة 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في ملف makefile الخاص بالمنتج. سيظل يتم إجراء عملية التحقّق من التوافق في وقت الإنشاء، ولكن عدم اجتياز عملية التحقّق لا يعني تعذُّر الإنشاء. بدلاً من ذلك، يؤدي تعذُّر إجراء عملية التحقّق إلى خفض نظام التصميم لمستوى فلتر برنامج dex2oat إلىverifyفي dexpreopt، ما يؤدي إلى إيقاف عملية الترجمة المسبقة بالكامل لهذه الوحدة. - للحصول على حلّ سريع وعالمي لسطر الأوامر، استخدِم متغيّر البيئة
RELAX_USES_LIBRARY_CHECK=true. ويؤدي هذا الخيار الوظيفة نفسها التي يؤديها الخيارPRODUCT_BROKEN_VERIFY_USES_LIBRARIES، ولكنّه مخصّص للاستخدام في سطر الأوامر. يتجاوز متغيّر البيئة متغيّر المنتج. - لإيجاد حل لإصلاح السبب الجذري للخطأ، يجب إعلام نظام التصميم بعلامات
<uses-library>في ملف البيان. يُظهر فحص رسالة الخطأ المكتبات التي تتسبّب في المشكلة (وكذلك فحصAndroidManifest.xmlأو ملف البيان داخل حزمة APK التي يمكن التحقّق منها باستخدامaapt dump badging $APK | grep uses-library).
بالنسبة إلى وحدات Android.bp:
ابحث عن المكتبة المفقودة في السمة
libsللوحدة. إذا كان هناك، يضيف Soong عادةً هذه المكتبات تلقائيًا، باستثناء الحالات الخاصة التالية:- المكتبة ليست مكتبة حزمة تطوير برامج (SDK) (يتم تعريفها على أنّها
java_libraryبدلاً منjava_sdk_library). - تحتوي المكتبة على اسم مكتبة مختلف (في ملف البيان) عن اسم الوحدة (في نظام التصميم).
لحلّ هذه المشكلة مؤقتًا، أضِف
provides_uses_lib: "<library-name>"في تعريف مكتبةAndroid.bp. للحصول على حل طويل الأمد، عليك إصلاح المشكلة الأساسية من خلال تحويل المكتبة إلى مكتبة حزمة تطوير برامج (SDK) أو إعادة تسمية الوحدة الخاصة بها.- المكتبة ليست مكتبة حزمة تطوير برامج (SDK) (يتم تعريفها على أنّها
إذا لم تقدّم الخطوة السابقة حلاً، أضِف
uses_libs: ["<library-module-name>"]للمكتبات المطلوبة، أوoptional_uses_libs: ["<library-module-name>"]للمكتبات الاختيارية إلى تعريفAndroid.bpللوحدة. تقبل هذه السمات قائمة بأسماء الوحدات. يجب أن يكون الترتيب النسبي للمكتبات في القائمة هو نفسه الترتيب في ملف البيان.
بالنسبة إلى وحدات Android.mk:
تحقَّق مما إذا كان للمكتبة اسم مختلف (في ملف البيان) عن اسم الوحدة (في نظام التصميم). إذا كان الأمر كذلك، يمكنك حلّ هذه المشكلة مؤقتًا عن طريق إضافة
LOCAL_PROVIDES_USES_LIBRARY := <library-name>في ملفAndroid.mkالخاص بالمكتبة، أو إضافةprovides_uses_lib: "<library-name>"في ملفAndroid.bpالخاص بالمكتبة (كلا الحالتين ممكنتان لأنّ وحدةAndroid.mkقد تعتمد على مكتبةAndroid.bp). لحلّ المشكلة على المدى الطويل، عليك إصلاح المشكلة الأساسية من خلال إعادة تسمية وحدة المكتبة.أضِف
LOCAL_USES_LIBRARIES := <library-module-name>للمكتبات المطلوبة، وأضِفLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>للمكتبات الاختيارية إلى تعريفAndroid.mkللوحدة. تقبل هذه السمات قائمة بأسماء الوحدات. يجب أن يكون الترتيب النسبي للمكتبات في القائمة هو نفسه الترتيب الوارد في ملف البيان.
خطأ في الإنشاء: مسار مكتبة غير معروف
إذا لم يتمكّن نظام الإنشاء من العثور على مسار إلى ملف <uses-library> DEX jar (إما مسار أثناء الإنشاء على المضيف أو مسار التثبيت على الجهاز)، سيتعذّر عادةً إنشاء التطبيق. قد يشير تعذُّر العثور على مسار إلى أنّ المكتبة تم ضبط إعداداتها بطريقة غير متوقّعة. يمكنك حلّ المشكلة مؤقتًا عن طريق إيقاف 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 ترتيب البحث في المكتبات عند تحليل فئات Java التي تستخدمها المكتبة أو التطبيق. ترتيب البحث مهم لأنّ المكتبات يمكن أن تحتوي على فئات مكرّرة، ويتم تحليل الفئة إلى أول تطابق.
CLC على الجهاز (وقت التشغيل)
ينشئ PackageManager (في frameworks/base) رمز CLC لتحميل وحدة Java على الجهاز فقط. يضيف هذا الإجراء المكتبات المُدرَجة في علامات <uses-library> في ملف البيان الخاص بالوحدة النمطية كعناصر CLC ذات مستوى أعلى.
بالنسبة إلى كل مكتبة مستخدَمة، تحصل PackageManager على جميع الاعتماديات <uses-library> (المحدّدة كعلامات في ملف البيان الخاص بهذه المكتبة) وتضيف رمز CLC متداخلًا لكل اعتمادية. تستمر هذه العملية بشكل متكرر إلى أن تصبح جميع عقد الأوراق في شجرة CLC التي تم إنشاؤها عبارة عن مكتبات بدون تبعيات <uses-library>.
لا يتعرّف PackageManager إلا على المكتبات المشتركة. يختلف تعريف "مشترَك" في هذا الاستخدام عن معناه المعتاد (كما في "مشترَك" مقابل "ثابت"). في نظام التشغيل Android،
المكتبات المشترَكة في Java هي تلك المدرَجة في إعدادات 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 [a-z ]+ mismatch'
يؤدي عدم التطابق إلى ضعف الأداء، لأنّه يجبر المكتبة أو التطبيق على إما أن يتم تحسينهما باستخدام dexopt أو أن يتم تشغيلهما بدون تحسينات (على سبيل المثال، قد يلزم استخراج رمز التطبيق في الذاكرة من حزمة APK، وهي عملية مكلفة للغاية).
يمكن أن تكون المكتبة المشتركة اختيارية أو مطلوبة. من وجهة نظر dexpreopt، يجب أن تكون المكتبة المطلوبة متوفّرة في مدّة التصميم (عدم توفّرها هو خطأ في الإنشاء). يمكن أن تكون المكتبة الاختيارية متوفرة أو غير متوفرة
في وقت الإنشاء: إذا كانت متوفرة، تتم إضافتها إلى CLC، ويتم تمريرها إلى dex2oat،
ويتم تسجيلها في ملف *.odex. في حال عدم توفّر مكتبة اختيارية، سيتم تخطّيها ولن تتم إضافتها إلى CLC. في حال عدم تطابق الحالة في وقت الإنشاء مع الحالة في وقت التشغيل (تتوفّر المكتبة الاختيارية في إحدى الحالتين دون الأخرى)، لن تتطابق رموز CLC في وقت الإنشاء مع رموز CLC في وقت التشغيل، وسيتم رفض الرمز البرمجي الذي تم تجميعه.
تفاصيل نظام الإنشاء المتقدّم (أداة إصلاح ملف البيان)
في بعض الأحيان، تكون علامات <uses-library> غير متوفّرة في ملف البيان المصدر الخاص بمكتبة أو تطبيق. ويمكن أن يحدث ذلك، على سبيل المثال، إذا بدأ أحد التبعيات المتعدّية للمكتبة أو التطبيق في استخدام علامة <uses-library> أخرى، ولم يتم تعديل ملف بيان المكتبة أو التطبيق لتضمينها.
يمكن أن يحسب Soong بعض علامات <uses-library> الناقصة لمكتبة أو تطبيق معيّنَين تلقائيًا، مثل مكتبات حزمة تطوير البرامج (SDK) في الإغلاق التعدّي للاعتمادية للمكتبة أو التطبيق. الإغلاق مطلوب لأنّ المكتبة (أو التطبيق) قد تعتمد على مكتبة ثابتة تعتمد على مكتبة حزمة تطوير البرامج (SDK)، وربما قد تعتمد مرة أخرى بشكل غير مباشر من خلال مكتبة أخرى.
لا يمكن احتساب جميع علامات <uses-library> بهذه الطريقة، ولكن عند الإمكان، من الأفضل السماح لأداة Soong بإضافة إدخالات البيان تلقائيًا، لأنّ ذلك يقلّل من الأخطاء ويسهّل الصيانة. على سبيل المثال، عندما تستخدم العديد من التطبيقات مكتبة ثابتة تضيف تبعية <uses-library> جديدة، يجب تحديث جميع التطبيقات، وهو أمر يصعب الحفاظ عليه.