إذا كان تطبيقك يتضمّن 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 لتنفيذ رموز التطبيقات. تقتصر التطبيقات تلقائيًا في Dalvik على ملف واحد
classes.dex لرمز البايت لكل حزمة APK. للتغلّب على هذا القيد، أضِف مكتبة multidex إلى ملف build.gradle أو build.gradle.kts على مستوى الوحدة:
أنيق
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 من واجهة برمجة التطبيقات) والإصدارات الأحدث وقت تشغيل يُسمى وقت تشغيل Android (ART) يتيح تحميل ملفات DEX متعددة من ملفات APK بشكل أصلي. تنفِّذ ART عملية الترجمة المسبقة عند تثبيت التطبيق، حيث يتم البحث عن ملفات classesN.dex وترجمتها إلى ملف OAT واحد لتنفيذه على جهاز Android. لذلك، إذا كان minSdkVersion
يساوي 21 أو أكثر، يتم تفعيل ميزة multidex تلقائيًا ولن تحتاج إلى مكتبة multidex.
لمزيد من المعلومات حول وقت التشغيل في Android 5.0، يمكنك قراءة مقالة وقت تشغيل Android (ART) وDalvik.
ملاحظة: عند تشغيل تطبيقك باستخدام "استوديو Android"، يتم تحسين الإصدار للأجهزة المستهدَفة التي تنشره عليها. ويشمل ذلك تفعيل multidex عندما تعمل الأجهزة المستهدَفة بالإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث. بما أنّ عملية التحسين هذه لا يتم تطبيقها إلا عند تفعيل تطبيقك باستخدام استوديو Android، قد يظل عليك ضبط بنية الإصدار لاستخدام ملفات DEX متعددة لتجنُّب الحد الأقصى البالغ 64 ألفًا.
تجنُّب الحدّ الأقصى البالغ 64 ألف حرف
قبل ضبط تطبيقك للسماح باستخدام 64 ألف مرجع أسلوب أو أكثر، اتّخِذ خطوات لتقليل العدد الإجمالي للمراجع التي يستدعيها رمز تطبيقك، بما في ذلك الأساليب المحدّدة بواسطة رمز تطبيقك أو المكتبات المضمّنة.
يمكن أن تساعدك الاستراتيجيات التالية في تجنُّب بلوغ الحد الأقصى لمراجع DEX:
- مراجعة التبعيات المباشرة والمتعدية لتطبيقك
- يجب التفكير مليًا في ما إذا كانت قيمة أي عنصر اعتمادي كبير للمكتبة تضمّنه في تطبيقك تفوق مقدار الرمز البرمجي الذي تتم إضافته إلى التطبيق، لأنّ أحد الأنماط الشائعة ولكنّها تسبّب مشاكل هو تضمين مكتبة كبيرة جدًا لأنّ بعض طرق الأدوات المساعدة كانت مفيدة. يمكن أن يساعد تقليل عدد العناصر التابعة لرموز تطبيقك في تجنُّب الحد الأقصى لعدد مراجع DEX.
- إزالة الرموز غير المستخدَمة باستخدام R8
- فعِّل خيار تقليل حجم الرموز لتشغيل R8 في إصداراتك. فعِّل عملية تقليل الحجم للمساعدة في ضمان عدم تضمين رموز غير مستخدَمة في حِزم APK. إذا تم ضبط إعدادات تقليل حجم الرموز بشكل صحيح، يمكنه أيضًا إزالة الرموز والموارد غير المستخدَمة من التبعيات.
يمكن أن يساعدك استخدام هذه التقنيات في تقليل الحجم الكلي لحزمة APK وتجنُّب الحاجة إلى استخدام multidex في تطبيقك.
إعداد تطبيقك لاستخدام multidex
ملاحظة: إذا تم ضبطminSdkVersion على 21 أو إصدار أحدث، يتم تفعيل ميزة multidex تلقائيًا
ولن تحتاج إلى مكتبة multidex.
إذا كانت قيمة minSdkVersion مضبوطة على 20 أو أقل، عليك استخدام مكتبة multidex وإجراء التعديلات التالية على مشروع تطبيقك:
-
عدِّل ملف
build.gradleعلى مستوى الوحدة لتفعيل ميزة multidex وإضافة مكتبة multidex كعنصر تابع، كما هو موضّح هنا:أنيق
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 36 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 36 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>
في حال إلغاء
Applicationالفئة، غيِّرها لتوسيعMultiDexApplication، كما يلي:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
إذا كنت تريد تجاوز فئة
Applicationولكن لا يمكن تغيير الصنف الأساسي، يمكنك بدلاً من ذلك تجاوز طريقةattachBaseContext()واستدعاءMultiDex.install(this)لتفعيل ملفات DEX متعددة: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 الرئيسي فقط، تستخدم واجهات برمجة تطبيقات ملفات DEX المتعددة أداة تحميل فئات خاصة للبحث في جميع ملفات DEX المتاحة عن طرقك.
قيود مكتبة multidex
تتضمّن مكتبة multidex بعض القيود المعروفة. عند دمج المكتبة في إعدادات تصميم تطبيقك، يجب مراعاة ما يلي:
- إنّ عملية تثبيت ملفات DEX أثناء بدء التشغيل على قسم البيانات في الجهاز معقّدة، وقد تؤدي إلى حدوث أخطاء "التطبيق لا يستجيب" (ANR) إذا كانت ملفات DEX الثانوية كبيرة. لتجنُّب هذه المشكلة، عليك تفعيل ميزة تقليل حجم الرموز لتصغير حجم ملفات DEX وإزالة أجزاء الرموز غير المستخدَمة.
- عند التشغيل على إصدارات أقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، لا يكفي استخدام multidex لتجنُّب حد linearalloc (المشكلة 37008143). تمت زيادة هذا الحد في الإصدار 4.0 من نظام التشغيل Android (المستوى 14 من واجهة برمجة التطبيقات)، ولكن لم يؤدِّ ذلك إلى حل المشكلة بشكل كامل.
في الإصدارات الأقدم من Android 4.0، قد تصل إلى الحد الأقصى المسموح به في linearalloc قبل الوصول إلى الحد الأقصى المسموح به لفهرس DEX. لذلك، إذا كنت تستهدف مستويات واجهة برمجة التطبيقات الأقل من 14، عليك إجراء اختبار شامل على هذه الإصدارات من النظام الأساسي، لأنّ تطبيقك قد يواجه مشاكل عند بدء التشغيل أو عند تحميل مجموعات معيّنة من الفئات.
يمكن أن يؤدي تخفيض حجم الرموز إلى الحدّ من هذه المشاكل أو ربما التخلص منها.
تعريف الفئات المطلوبة في ملف DEX الأساسي
عند إنشاء كل ملف DEX لتطبيق يستخدم ملفات DEX متعددة، تنفّذ أدوات الإنشاء عملية اتخاذ قرارات معقّدة لتحديد الفئات المطلوبة في ملف 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
بعد ذلك، يمكنك تعريف هذا الملف لنوع إصدار على النحو التالي:
أنيق
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
تحسين multidex في إصدارات التطوير
يتطلّب إعداد multidex زيادة كبيرة في وقت معالجة الإصدار، لأنّ نظام التصميم يجب أن يتّخذ قرارات معقّدة بشأن الفئات التي يجب تضمينها في ملف DEX الأساسي والفئات التي يمكن تضمينها في ملفات DEX الثانوية. وهذا يعني أنّ عمليات الإنشاء التزايدي باستخدام multidex تستغرق عادةً وقتًا أطول ويمكن أن تؤدي إلى إبطاء عملية التطوير.
للتخفيف من أوقات الإنشاء التزايدي الأطول، استخدِم التحويل المسبق إلى ملفات .dex لإعادة استخدام ناتج ملفات DEX متعددة بين الإصدارات.
تعتمد عملية التحويل إلى ملفات .dex المسبق على تنسيق وقت تشغيل Android (ART) المتوفّر فقط على نظام التشغيل Android 5.0
(مستوى واجهة برمجة التطبيقات 21) والإصدارات الأحدث. إذا كنت تستخدم "استوديو Android"، تستخدم بيئة التطوير المتكاملة تلقائيًا عملية التحويل إلى ملفات .dex عند نشر تطبيقك على جهاز يعمل بالإصدار 5.0 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 21) أو الإصدارات الأحدث.
ومع ذلك، إذا كنت تنفّذ عمليات إنشاء Gradle من سطر الأوامر، عليك ضبط minSdkVersion على 21 أو إصدار أحدث لتفعيل تحويل إلى ملفات .dex.
minSdkVersion، كما هو موضّح أدناه:
أنيق
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 متعددة
عند كتابة اختبارات الأجهزة للتطبيقات التي تستخدم ملفات DEX متعددة، لن تحتاج إلى ضبط أي إعدادات إضافية
إذا كنت تستخدم
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); ... }