إذا كان تطبيقك يحتوي على 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 قابل للتنفيذ (DEX). توضِّح هذه الصفحة كيفية تجاوز هذا القيد من خلال تفعيل إعدادات التطبيق المعروفة باسم multidex، والتي تسمح لتطبيقك بإنشاء ملفات DEX متعددة وقراءتها.
لمحة عن الحد الأقصى المسموح به للمراجع الذي يبلغ 64 كيلوبايت
تحتوي ملفات تطبيق Android (APK) على ملفات رمز برمجي قابل للتنفيذ في شكل ملفات Dalvik القابلة للتنفيذ (DEX)، والتي تحتوي على الرمز المجمّع المستخدَم لتشغيل تطبيقك. تحدّ مواصفات Dalvik القابلة للتنفيذ من إجمالي عدد الطرق التي يمكن الإشارة إليها في ملف DEX واحد إلى 65,536، بما في ذلك طرق إطار عمل Android وطرق المكتبة والطُرق الواردة في الرمز البرمجي الخاص بك.
في سياق علوم الكمبيوتر، يشير مصطلح الكيلو أو K إلى 1024 (أو 2^10). وبما أنّ 65,536 يساوي 64×1024، يُشار إلى هذا الحدّ باسم _الحدّ المرجعي البالغ 64 كيلوبايت_.إتاحة حِزم Multidex قبل الإصدار 5.0 من Android
تستخدم إصدارات النظام الأساسي الأقدم من Android 5.0 (المستوى 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 الإضافية والرمز البرمجي الذي تحتوي عليه. للاطّلاع على الإصدارات الحالية لهذه المكتبة، يُرجى الاطّلاع على إصدارات حِزم APK المتعددة.
لمزيد من التفاصيل، اطّلِع على القسم المعنيّ بكيفية ضبط تطبيقك للاستفادة من ميزة "متعدد الإصدارات".دعم Multidex لنظام التشغيل Android 5.0 والإصدارات الأحدث
يستخدم نظام التشغيل Android 5.0 (المستوى 21 لواجهة برمجة التطبيقات) والإصدارات الأحدث بيئة تشغيل تُعرف باسم ART تسمح
بشكلٍ أساسي بتحميل ملفات DEX متعددة من ملفات APK. تنفّذ تقنية ART
عملية التجميع المسبق أثناء تثبيت التطبيق، وتبحث عن
ملفات classesN.dex
وتجمعها في
ملف OAT واحد
لينفّذها جهاز Android. لذلك، إذا كان الإصدار minSdkVersion
هو 21 أو إصدار أحدث، يتم تفعيل أداة MultiDex تلقائيًا ولن تحتاج إلى مكتبة MultiDex.
لمزيد من المعلومات عن وقت تشغيل Android 5.0 ، يُرجى الاطّلاع على Android Runtime (ART) وDalvik.
ملاحظة: عند تشغيل تطبيقك باستخدام Android Studio، يتم تحسين الإصدار للأجهزة المستهدفة التي يتم نشره عليها. ويشمل ذلك تفعيل ميزة "متعدد الحِزم" عندما تعمل الأجهزة المستهدَفة بالإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث. بما أنّه لا يتم تطبيق هذا التحسين إلا عند نشر تطبيقك باستخدام IDE IDE Android Studio، قد يظل عليك ضبط إصدار الإصدار لميزة "حِزم APK متعددة" لتجنُّب الحد الأقصى البالغ 64 كيلوبايت.
تجنُّب الحدّ الأقصى البالغ 64 كيلوبايت
قبل إعداد تطبيقك لإتاحة استخدام مراجع طُرق بدقة 64K أو أكثر، عليك اتّخاذ خطوات لتقليل إجمالي عدد المراجع التي يتم استدعاؤها من خلال رمز التطبيق، بما في ذلك الطرق التي يحدّدها هذا الرمز أو المكتبات المضمّنة.
يمكن أن تساعدك الاستراتيجيات التالية في تجنُّب تجاوز الحدّ الأقصى لمرجع DEX:
- مراجعة التبعيات المباشرة وغير المباشرة لتطبيقك
- ننصحك بالتفكير في ما إذا كانت قيمة أي مكتبة كبيرة تعتمد عليها في تطبيقك تفوق مقدار الرمز البرمجي الذي تتم إضافته إلى التطبيق. ومن الأنماط الشائعة ولكنّها مشكلة تضمين مكتبة كبيرة جدًا لأنّ بعض طرق المرافق كانت مفيدة. يمكن أن يساعدك تقليل التبعيات في رمز تطبيقك في كثير من الأحيان تجنُّب الحد الأقصى لمرجع DEX.
- إزالة الرموز غير المستخدَمة باستخدام R8
- فعِّل ميزة تقليل حجم الرموز لتشغيل R8 في عمليات إنشاء الإصدارات. يمكنك تفعيل ميزة تقليل حجم الرموز للمساعدة في التأكّد من عدم إرسال رمز غير مستخدَم مع حِزم APK. في حال ضبط عملية تصغير الرموز بشكل صحيح، يمكنها أيضًا إزالة الرموز والموارد غير المستخدَمة من التبعيات.
يمكن أن يساعدك استخدام هذه التقنيات في تقليل الحجم الإجمالي لحزمة APK و تجنُّب الحاجة إلى حزمة multidex في تطبيقك.
ضبط تطبيقك للاستفادة من ميزة "متعدد الإصدارات"
ملاحظة: إذا تم ضبطminSdkVersion
على 21 أو أعلى، يتم تفعيل Multidex تلقائيًا
ولن تحتاج إلى مكتبة الملفات المتعدّدة.
إذا تم ضبط minSdkVersion
على 20 أو أقل، يجب استخدام مكتبة Multidex وإجراء التعديلات التالية على مشروع تطبيقك:
-
عدِّل ملف
build.gradle
على مستوى الوحدة لتفعيل Multidex وأضف مكتبة الوسائط المتعددة كملحق، كما هو موضَّح هنا:رائع
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
الفئة ولكن تعذّر تغيير الفئة الأساسية، بدلاً من ذلك، ألغِ طريقة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
الرئيسي فقط، تستخدِم واجهات برمجة تطبيقات حِزم DEX المتعددة أداة تحميل فئة خاصة للبحث في جميع ملفّات classes.dex
المتاحة عن طريقطك.
قيود مكتبة multidex
تفرض مكتبة multidex بعض القيود المعروفة. عند دمج المكتبة في إعدادات إنشاء تطبيقك، يجب مراعاة ما يلي:
- إنّ تثبيت ملفات DEX أثناء بدء التشغيل على قسم بيانات الجهاز معقّد، ويمكن أن يؤدي إلى حدوث أخطاء "التطبيق لا يستجيب" (ANR) إذا كانت ملفات DEX الثانوية كبيرة. لتجنُّب هذه المشكلة، يمكنك تفعيل تخفيض حجم الرموز لتقليل حجم ملفات DEX وإزالة الأجزاء غير المستخدَمة من الرمز.
- وعند تشغيل هذه التطبيقات على إصدارات تسبق Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، لا يكفي استخدام
Multidex للتحايل على الحدّ الأقصى الخطي (المشكلة 37008143). تم رفع هذا الحدّ في
الإصدار 4.0 من نظام التشغيل Android (المستوى 14 من واجهة برمجة التطبيقات)، ولكنّ ذلك لم يحلّ المشكلة بالكامل.
في الإصدارات الأقدم من Android 4.0، قد تصل إلى الحد الأقصى لمساحة التخزين في ذاكرة التخزين المؤقت قبل بلوغ الحد الأقصى لفهرسة DEX. لذلك، إذا كنت تستهدِف مستويات أقل من واجهة برمجة التطبيقات 14، عليك إجراء اختبار شامل على هذه الإصدارات من المنصة، لأنّ تطبيقك قد يواجه مشاكل عند بدء التشغيل أو عند تحميل مجموعات معيّنة من الفئات.
يمكن أن تقلِّل تصغير الرموز البرمجية من هذه المشاكل أو تقضي عليها.
تحديد الفئات المطلوبة في ملف DEX الأساسي
عند إنشاء كل ملف DEX لتطبيق متعدّد DEX، تُجري أدوات الإنشاء عملية اتخاذ قرارات معقدة لتحديد الفئات المطلوبة في ملف DEX الأساسي كي يتمكّن تطبيقك من البدء بنجاح. إذا لم يتم توفير أي فئة مطلوبة
أثناء بدء التشغيل في ملف DEX الأساسي، سيتعطّل تطبيقك
مع ظهور الخطأ java.lang.NoClassDefFoundError
.
تتعرّف أدوات التصميم على مسارات الرمز البرمجي الذي يمكن الوصول إليه مباشرةً من رمز تطبيقك. ومع ذلك، يمكن أن تحدث هذه المشكلة عندما تكون مسارات الرموز البرمجية أقل ظهورًا، مثل عندما تحتوي المكتبة التي تستخدمها على تبعيات معقّدة. على سبيل المثال، إذا كان الرمز يستخدم ميزة "الاستكشاف الذاتي" أو استدعاء methods من Java من الرمز الأصلي، قد لا يتم التعرّف على هذه الفئات على أنّها مطلوبة في ملف DEX الأساسي.
إذا تلقّيت الخطأ java.lang.NoClassDefFoundError
،
عليك تحديد الفئات الإضافية المطلوبة يدويًا في ملف DEX
الأساسي من خلال الإفصاح عنها باستخدام السمة multiDexKeepProguard
في نوع الإنشاء. وإذا تمت مطابقة فئة في ملف multiDexKeepProguard
، تتم إضافة هذه الفئة إلى ملف DEX الأساسي.
سمة MultiDexKeepProguard
يستخدم ملف 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") ... } } }
تحسين أداء متعدد القنوات في إصدارات التطوير
تتطلّب عملية إعداد حِزم متعددة بتنسيق DEX وقتًا متزايدًا بشكل كبير لمعالجة عملية الإنشاء، لأنّ نظام الإنشاء يجب أن يتّخذ قرارات معقّدة بشأن الفئات التي يجب تضمينها في ملف DEX الأساسي والفئات التي يمكن تضمينها في ملفات DEX الثانوية. وهذا يعني أنّ الإصدارات التدريجية التي تستخدم Multidex تستغرق عادةً وقتًا أطول وقد تؤدي إلى إبطاء عملية التطوير.
لتقليل أوقات الإنشاء المتزايدة، استخدِم
المعالجة المسبقة لملف dex لإعادة استخدام ناتج حِزم multidex بين عمليات الإنشاء.
تعتمد ميزة "الترميز المُسبَق" على تنسيق ART غير المتوفّر إلا على الإصدار 5.0
من Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث. إذا كنت تستخدم Android Studio، ستستخدم بيئة التطوير المتكاملة تلقائيًا ميزة "الترميز المُسبق"
عند نشر تطبيقك على جهاز يعمل بالإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) أو إصدار أحدث.
ومع ذلك، إذا كنت تُشغّل عمليات إنشاء Gradle من سطر الأوامر، عليك ضبط
minSdkVersion
على 21 أو إصدار أحدث لتفعيل ميزة "المعالجة المسبقة للغة برمجة Java".
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" أو سطر الأوامر، يُرجى الاطّلاع على مقالة تحسين سرعة الإصدار. لمزيد من المعلومات حول استخدام الأسعار المتغيرة للإصدار، يُرجى الاطّلاع على مقالة ضبط الأسعار المتغيرة للإصدار.
ملاحظة: إذا كانت لديك أنواع مختلفة من عمليات الإنشاء لمختلف
احتياجات حِزم APK المتعددة، يمكنك تقديم ملف بيان مختلف لكل نوع
حتى لا يغيّر الملف المخصّص لمستوى واجهة برمجة التطبيقات 20 والإصدارات الأقدم سوى اسم علامة
<application>
. يمكنك أيضًا
إنشاء فئة فرعية مختلفة من Application
لكل صيغة لكي تتمكّن
فقط الفئة الفرعية للمستوى 20 من واجهة برمجة التطبيقات والإصدارات الأقدم من توسيع فئة MultiDexApplication
أو
استدعاء MultiDex.install(this)
.
اختبار تطبيقات Multidex
عند كتابة اختبارات أداة القياس لتطبيقات حِزم متعددة، لن تكون هناك حاجة إلى ضبط أي إعدادات إضافية
إذا كنت تستخدم أداة قياس
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); ... }