إذا كان تطبيقك يتضمّن 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 تساوي 64x1024، يُشار إلى هذا الحد باسم _الحد الأقصى المرجعي البالغ 64 ألف_.إتاحة استخدام Multidex قبل الإصدار 5.0 من نظام التشغيل Android
تستخدم إصدارات النظام الأساسي السابقة للإصدار 5.0 من Android (المستوى 21 لواجهة برمجة التطبيقات) وقت تشغيل Dalvik لتنفيذ رمز التطبيق. تفرض Dalvik تلقائيًا على التطبيقات حدًا أقصى يبلغ ملف classes.dex
واحد لرمز البايت لكل حزمة APK. للتغلّب على هذا القيد، أضِف مكتبة 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.
لمزيد من المعلومات حول وقت التشغيل في Android 5.0، يُرجى قراءة مقالة وقت تشغيل Android (ART) وDalvik.
ملاحظة: عند تشغيل تطبيقك باستخدام Android Studio، يتم تحسين الإصدار للأجهزة المستهدَفة التي تنشره عليها. ويشمل ذلك تفعيل 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>
في حال إلغاء
Application
الفئة، غيِّرها لتوسيعMultiDexApplication
، كما يلي:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
إذا كنت تريد إلغاء
Application
class ولكن لا يمكن تغيير الفئة الأساسية، يمكنك بدلاً من ذلك إلغاء طريقة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 من واجهة برمجة التطبيقات)، لا يكفي استخدام multidex لتجنُّب حد 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 Studio، تستخدم بيئة التطوير المتكاملة (IDE) عملية إنشاء ملفات dex مسبقًا تلقائيًا عند نشر تطبيقك على جهاز يعمل بالإصدار 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); ... }