إذا كان تطبيقك يتضمّن minSdk من المستوى 20 لواجهة برمجة التطبيقات أو مستوى أقل وكان تطبيقك والمكتبات التي يشير إليها يتجاوزان 65,536 طريقة، سيظهر لك خطأ الإنشاء التالي الذي يشير إلى أنّ تطبيقك قد بلغ الحد الأقصى لبنية الإنشاء في Android:
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
تُبلغ الإصدارات القديمة من نظام التصميم عن خطأ مختلف، وهو مؤشر على المشكلة نفسها:
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
تعرض حالات الخطأ هذه الرقم الشائع 65536. يمثّل هذا الرقم إجمالي عدد المراجع التي يمكن استدعاؤها من خلال الرمز البرمجي في ملف واحد من ملفات Dalvik Executable (DEX) لرمز البايت. توضّح هذه الصفحة كيفية تجاوز هذا الحدّ من خلال تفعيل إعداد تطبيق يُعرف باسم multidex، ما يتيح لتطبيقك إنشاء ملفات DEX متعددة وقراءتها.
لمحة عن الحدّ الأقصى المسموح به لعدد المراجع وهو 64 ألف مرجع
تحتوي ملفات تطبيقات Android (APK) على ملفات رمز بايت تنفيذي في شكل ملفات Dalvik Executable (DEX)، والتي تحتوي على الرمز المجمَّع المستخدَم لتشغيل تطبيقك. يحدّد مواصفات Dalvik Executable إجمالي عدد الطرق التي يمكن الرجوع إليها في ملف DEX واحد بـ 65,536 طريقة، بما في ذلك طرق إطار عمل Android وطرق المكتبة والطرق في الرمز الخاص بك.
في سياق علوم الكمبيوتر، يشير مصطلح كيلو أو K إلى 1024 (أو 2^10). بما أنّ 65,536 تساوي 64 × 1024، يُشار إلى هذا الحد باسم _الحد الأقصى المرجعي البالغ 64 ألف_.إتاحة استخدام Multidex قبل الإصدار 5.0 من نظام التشغيل Android
تستخدم إصدارات النظام الأساسي السابقة للإصدار 5.0 من Android (المستوى 21 لواجهة برمجة التطبيقات) وقت تشغيل Dalvik لتنفيذ رمز التطبيق. يقتصر عدد ملفات رمز البايت في كل حزمة APK على ملف واحد في Dalvik.classes.dex للتغلّب على هذا القيد، أضِف مكتبة multidex إلى ملف build.gradle أو build.gradle.kts على مستوى الوحدة:
Groovy
dependencies { def multidex_version = "2.0.1" implementation "androidx.multidex:multidex:$multidex_version" }
Kotlin
dependencies { val multidex_version = "2.0.1" implementation("androidx.multidex:multidex:$multidex_version") }
تصبح هذه المكتبة جزءًا من ملف DEX الأساسي لتطبيقك، ثم تدير إمكانية الوصول إلى ملفات DEX الإضافية والرمز البرمجي الذي تحتويه. للاطّلاع على الإصدارات الحالية لهذه المكتبة، راجِع إصدارات multidex.
لمزيد من التفاصيل، راجِع القسم حول كيفية ضبط تطبيقك لاستخدام multidex.توافق Multidex مع الإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث
يستخدم الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث وقت تشغيل يُسمى ART يتيح تحميل ملفات DEX متعددة من ملفات APK بشكل أصلي. تنفِّذ ART عملية الترجمة المسبقة عند تثبيت التطبيق، حيث يتم البحث عن ملفات classesN.dex وترجمتها إلى ملف OAT واحد لتنفيذه على جهاز Android. لذلك، إذا كان minSdkVersion
يساوي 21 أو أكثر، يتم تفعيل ميزة multidex تلقائيًا ولن تحتاج إلى مكتبة multidex.
لمزيد من المعلومات حول وقت التشغيل في الإصدار 5.0 من نظام التشغيل Android، يُرجى قراءة مقالة وقت تشغيل Android (ART) وDalvik.
ملاحظة: عند تشغيل تطبيقك باستخدام "استوديو Android"، يتم تحسين الإصدار للأجهزة المستهدَفة التي تنشره عليها. ويشمل ذلك تفعيل multidex عندما تعمل الأجهزة المستهدَفة بالإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث. بما أنّ هذا التحسين لا يتم تطبيقه إلا عند نشر تطبيقك باستخدام Android Studio، قد يظل عليك ضبط إصدارك لاستخدام multidex لتجنُّب الحد الأقصى البالغ 64 ألفًا.
تجنُّب الحدّ الأقصى البالغ 64 ألف حرف
قبل ضبط تطبيقك لإتاحة استخدام 64 ألف مرجع أسلوب أو أكثر، اتّخِذ خطوات لتقليل العدد الإجمالي للمراجع التي يستدعيها رمز تطبيقك، بما في ذلك الأساليب المحدّدة بواسطة رمز تطبيقك أو المكتبات المضمّنة.
يمكن أن تساعدك الاستراتيجيات التالية في تجنُّب تجاوز الحد الأقصى لعدد المراجع في DEX:
- مراجعة التبعيات المباشرة والمتعدية لتطبيقك
- عليك تحديد ما إذا كانت قيمة أي عنصر اعتمادي كبير للمكتبة تضمّنه في تطبيقك تفوق مقدار الرمز البرمجي الذي تتم إضافته إلى التطبيق، إذ إنّ أحد الأنماط الشائعة ولكنّها تسبّب مشاكل هو تضمين مكتبة كبيرة جدًا لأنّ بعض طرق الأدوات المساعدة كانت مفيدة. يمكن أن يساعد تقليل عدد العناصر التابعة لرموز تطبيقك في تجنُّب الحد الأقصى لعدد مراجع DEX.
- إزالة الرموز غير المستخدَمة باستخدام R8
- فعِّل خيار تقليل حجم الرموز لتشغيل R8 في إصداراتك. فعِّل عملية تقليل الحجم للمساعدة في ضمان عدم تضمين رموز غير مستخدَمة في حِزم APK. إذا تم ضبط إعدادات تقليل حجم الرموز بشكل صحيح، يمكنه أيضًا إزالة الرموز والموارد غير المستخدَمة من التبعيات.
يمكن أن يساعدك استخدام هذه التقنيات في تقليل الحجم الكلي لحزمة APK وتجنُّب الحاجة إلى استخدام multidex في تطبيقك.
إعداد تطبيقك لاستخدام multidex
ملاحظة: إذا تم ضبطminSdkVersion على 21 أو إصدار أحدث، يتم تفعيل ميزة multidex تلقائيًا
ولن تحتاج إلى مكتبة multidex.
إذا كانت قيمة minSdkVersion مضبوطة على 20 أو أقل، عليك استخدام مكتبة multidex وإجراء التعديلات التالية على مشروع تطبيقك:
-
عدِّل ملف
build.gradleعلى مستوى الوحدة لتفعيل multidex وإضافة مكتبة multidex كعنصر تابع، كما هو موضّح هنا:Groovy
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 33 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 33 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
- استنادًا إلى ما إذا كنت ستتجاهل الفئة
Application، نفِّذ أحد الإجراءات التالية:إذا لم يتم إلغاء الفئة
Application، عدِّل ملف البيان لضبطandroid:nameفي العلامة<application>على النحو التالي:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="androidx.multidex.MultiDexApplication" > ... </application> </manifest>
في حال تجاهلت
Applicationclass، غيِّرها لتوسيعMultiDexApplication، كما يلي:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
إذا كنت تريد إلغاء
Applicationclass ولكن لا يمكن تغيير الفئة الأساسية، يمكنك بدلاً من ذلك إلغاء طريقةattachBaseContext()واستدعاءMultiDex.install(this)لتفعيل multidex:Kotlin
class MyApplication : SomeOtherApplication() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } }
Java
public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
تنبيه: لا تنفِّذ
MultiDex.install()أو أي رمز آخر من خلال الانعكاس أو JNI قبل اكتمالMultiDex.install(). لن تتتبّع أداة Multidex عمليات الاستدعاء هذه، ما يؤدي إلى حدوث أخطاءClassNotFoundExceptionأو أخطاء في التحقّق بسبب تقسيم غير صحيح للفئات بين ملفات DEX.
عند إنشاء تطبيقك، ستنشئ أدوات إنشاء Android ملف DEX أساسيًا (classes.dex) وملفات DEX مساعدة (classes2.dex وclasses3.dex وما إلى ذلك) حسب الحاجة.
بعد ذلك، يجمع نظام الإنشاء جميع ملفات DEX في حزمة APK.
أثناء وقت التشغيل، بدلاً من البحث في ملف classes.dex الرئيسي فقط، تستخدم واجهات برمجة تطبيقات multidex أداة تحميل فئات خاصة للبحث في جميع ملفات DEX المتاحة عن طرقك.
قيود مكتبة multidex
تتضمّن مكتبة multidex بعض القيود المعروفة. عند دمج المكتبة في إعدادات إنشاء تطبيقك، يجب مراعاة ما يلي:
- إنّ عملية تثبيت ملفات DEX أثناء بدء التشغيل على قسم البيانات في الجهاز معقّدة، وقد تؤدي إلى حدوث أخطاء "التطبيق لا يستجيب" (ANR) إذا كانت ملفات DEX الثانوية كبيرة. لتجنُّب هذه المشكلة، عليك تفعيل ميزة تقليل حجم الرموز لتصغير حجم ملفات DEX وإزالة أجزاء الرموز غير المستخدَمة.
- عند التشغيل على إصدارات أقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، لا يكفي استخدام ملفات DEX المتعددة لتجنُّب الحد الأقصى المسموح به في linearalloc (المشكلة 37008143). تمت زيادة هذا الحد في الإصدار 4.0 من نظام التشغيل Android (المستوى 14 من واجهة برمجة التطبيقات)، ولكن لم يؤدِّ ذلك إلى حل المشكلة بشكل كامل.
في الإصدارات الأقدم من Android 4.0، قد تصل إلى الحد الأقصى المسموح به في linearalloc قبل الوصول إلى الحد الأقصى المسموح به لفهرس DEX. لذا، إذا كنت تستهدف مستويات واجهة برمجة التطبيقات الأقل من 14، عليك إجراء اختبار شامل على هذه الإصدارات من النظام الأساسي، لأنّ تطبيقك قد يواجه مشاكل عند بدء التشغيل أو عند تحميل مجموعات معيّنة من الفئات.
يمكن أن يؤدي تصغير الرموز إلى الحدّ من هذه المشاكل أو إزالتها نهائيًا.
تعريف الفئات المطلوبة في ملف DEX الأساسي
عند إنشاء كل ملف DEX لتطبيق يستخدم ميزة multidex، تنفّذ أدوات الإنشاء عملية اتخاذ قرارات معقّدة لتحديد الفئات المطلوبة في ملف DEX الأساسي حتى يتمكّن تطبيقك من البدء بنجاح. إذا لم يتم توفير أي فئة مطلوبة أثناء بدء التشغيل في ملف DEX الأساسي، سيتعطّل تطبيقك مع ظهور الخطأ java.lang.NoClassDefFoundError.
تتعرّف أدوات الإنشاء على مسارات الرموز البرمجية التي يتم الوصول إليها مباشرةً من رمز تطبيقك. ومع ذلك، يمكن أن تحدث هذه المشكلة عندما تكون مسارات الرموز أقل وضوحًا، مثلاً عندما تحتوي مكتبة تستخدمها على تبعيات معقّدة. على سبيل المثال، إذا كان الرمز البرمجي يستخدم الاستبطان أو الاستدعاء لطُرق Java من الرمز البرمجي الأصلي، قد لا يتم التعرّف على هذه الفئات على أنّها مطلوبة في ملف DEX الأساسي.
إذا تلقّيت الخطأ java.lang.NoClassDefFoundError، عليك تحديد الفئات الإضافية المطلوبة في ملف DEX الأساسي يدويًا من خلال تعريفها باستخدام السمة multiDexKeepProguard في نوع الإصدار. إذا تم العثور على تطابق لفئة في ملف multiDexKeepProguard، تتم إضافة هذه الفئة إلى ملف DEX الأساسي.
multiDexKeepProguard property
يستخدم الملف multiDexKeepProguard التنسيق نفسه المستخدَم في ProGuard ويتوافق مع جميع قواعد ProGuard. لمزيد من المعلومات حول كيفية تخصيص المحتوى الذي يتم الاحتفاظ به في تطبيقك، راجِع تخصيص الرمز الذي سيتم الاحتفاظ به.
يجب أن يحتوي الملف الذي تحدّده في multiDexKeepProguard على خيارات -keep بأي صيغة ProGuard صالحة. على سبيل المثال:
-keep com.example.MyClass.class. يمكنك إنشاء ملف باسم
multidex-config.pro يبدو على النحو التالي:
-keep class com.example.MyClass -keep class com.example.MyClassToo
إذا أردت تحديد جميع الفئات في حزمة، سيبدو الملف على النحو التالي:
-keep class com.example.** { *; } // All classes in the com.example package
بعد ذلك، يمكنك تعريف هذا الملف لنوع إصدار على النحو التالي:
Groovy
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
تحسين multidex في إصدارات التطوير
يتطلّب إعداد multidex زيادة كبيرة في وقت معالجة الإصدار، لأنّ نظام الإصدار يجب أن يتّخذ قرارات معقّدة بشأن الفئات التي يجب تضمينها في ملف DEX الأساسي والفئات التي يمكن تضمينها في ملفات DEX الثانوية. وهذا يعني أنّ عمليات الإنشاء التزايدي باستخدام multidex تستغرق عادةً وقتًا أطول ويمكن أن تؤدي إلى إبطاء عملية التطوير.
لتقليل أوقات الإنشاء التزايدي الأطول، استخدِم عملية التحويل المسبق إلى DEX لإعادة استخدام ناتج multidex بين عمليات الإنشاء.
تعتمد عملية التحويل المسبق إلى رمز DEX على تنسيق ART المتوفّر فقط على الإصدار 5.0 من نظام التشغيل Android
(المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث. إذا كنت تستخدم "استوديو Android"، تستخدم بيئة التطوير المتكاملة تلقائيًا عملية pre-dexing عند نشر تطبيقك على جهاز يعمل بالإصدار 5.0 من نظام التشغيل Android (المستوى 21 لواجهة برمجة التطبيقات) أو إصدار أحدث.
ومع ذلك، إذا كنت تنفّذ عمليات إنشاء Gradle من سطر الأوامر، عليك ضبط minSdkVersion على 21 أو إصدار أحدث لتفعيل عملية إنشاء ملفات DEX المسبقة.
minSdkVersion، كما هو موضّح أدناه:
Groovy
android { defaultConfig { ... multiDexEnabled true // The default minimum API level you want to support. minSdkVersion 15 } productFlavors { // Includes settings you want to keep only while developing your app. dev { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdkVersion 21 } prod { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... multiDexEnabled = true // The default minimum API level you want to support. minSdk = 15 } productFlavors { // Includes settings you want to keep only while developing your app. create("dev") { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdk = 21 } create("prod") { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
لمزيد من المعلومات حول الاستراتيجيات التي تساعد في تحسين سرعات الإنشاء من "استوديو Android" أو سطر الأوامر، يمكنك الاطّلاع على تحسين سرعة الإنشاء. لمزيد من المعلومات حول استخدام تنويعات الإصدارات، يُرجى الاطّلاع على إعداد تنويعات الإصدارات.
ملاحظة: إذا كانت لديك تنويعات إصدار مختلفة لتلبية احتياجات مختلفة من multidex، يمكنك توفير ملف بيان مختلف لكل تنويعة، وبالتالي لن يغيّر سوى الملف الخاص بالمستوى 20 من واجهة برمجة التطبيقات والإصدارات الأقدم اسم العلامة <application>. يمكنك أيضًا إنشاء فئة فرعية مختلفة من Application لكل صيغة، بحيث لا توسّع الفئة الفرعية للمستوى 20 من واجهة برمجة التطبيقات والإصدارات الأقدم الفئة MultiDexApplication أو تستدعي MultiDex.install(this).
اختبار تطبيقات تتضمّن ملفات DEX متعددة
عند كتابة اختبارات الأجهزة للتطبيقات التي تستخدم حِزم APK متعددة، لن تحتاج إلى ضبط أي إعدادات إضافية
إذا كنت تستخدم
MonitoringInstrumentation أو
AndroidJUnitRunner
للأجهزة. إذا كنت تستخدم Instrumentation أخرى،
عليك إلغاء طريقة onCreate() الخاصة بها باستخدام الرمز التالي:
Kotlin
fun onCreate(arguments: Bundle) { MultiDex.install(targetContext) super.onCreate(arguments) ... }
Java
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }