إذا كان تطبيقك يحتوي على 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 القابلة للتنفيذ (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 التطبيقات تلقائيًا في استخدام ملف bytecode واحد فقط
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 compiling pre-compilation (الترجمة المسبقة) في وقت تثبيت التطبيق، وذلك من خلال البحث عن ملفات classesN.dex
compiling وتحويلها إلى ملف OAT compiling واحد لكي ينفِّذه جهاز Android. لذلك، إذا كان الإصدار minSdkVersion
هو 21 أو إصدار أحدث، يتم تفعيل أداة MultiDex تلقائيًا ولن تحتاج إلى مكتبة MultiDex.
لمزيد من المعلومات عن وقت تشغيل Android 5.0 ، يُرجى الاطّلاع على مقالة Android Runtime (ART) وDalvik.
ملاحظة: عند تشغيل تطبيقك باستخدام Android Studio، يتم تحسين الإصدار للأجهزة المستهدفة التي يتم نشره عليها. ويشمل ذلك تفعيل ميزة "متعدد الحِزم" عندما تعمل الأجهزة المستهدَفة بالإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث. بما أنّه لا يتم تطبيق هذا التحسين إلا عند نشر تطبيقك باستخدام IDE IDE Android Studio، قد تحتاج إلى ضبط إصدار الإصدار لميزة "حِزم APK متعددة" لتجنُّب الحد الأقصى البالغ 64 كيلوبايت.
تجنُّب الحدّ الأقصى البالغ 64 كيلوبايت
قبل ضبط تطبيقك لتفعيل استخدام 64 كيلوبايت أو أكثر من مراجع الأساليب، عليك اتّخاذ خطوات لتقليل العدد الإجمالي للمراجع التي يستدعيها رمز تطبيقك، بما في ذلك الأساليب التي يحدّدها رمز تطبيقك أو المكتبات المضمّنة.
يمكن أن تساعدك الاستراتيجيات التالية في تجنُّب تجاوز الحدّ الأقصى لمرجع DEX:
- مراجعة التبعيات المباشرة وغير المباشرة لتطبيقك
- ننصحك بالتفكير في ما إذا كانت قيمة أي مكتبة كبيرة تعتمد عليها في تطبيقك تفوق مقدار الرمز البرمجي الذي تتم إضافته إلى التطبيق. ومن الأنماط الشائعة ولكنّها مشكلة تضمين مكتبة كبيرة جدًا لأنّ بعض طرق المرافق كانت مفيدة. يمكن أن يساعدك تقليل التبعيات في رمز تطبيقك في كثير من الأحيان تجنُّب الحد الأقصى لمرجع DEX.
- إزالة الرموز غير المستخدَمة باستخدام R8
- فعِّل ميزة تقليل حجم الرموز لتشغيل R8 في عمليات إنشاء الإصدارات. يمكنك تفعيل ميزة تقليل حجم الرموز للمساعدة في التأكّد من عدم إرسال رمز غير مستخدَم مع حِزم APK. في حال ضبط عملية تصغير الرموز بشكل صحيح، يمكنها أيضًا إزالة الرموز والموارد غير المستخدَمة من التبعيات.
يمكن أن يساعدك استخدام هذه التقنيات في تقليل الحجم الإجمالي لحزمة APK و تجنُّب الحاجة إلى حزمة multidex في تطبيقك.
ضبط تطبيقك للاستفادة من ميزة "متعدد الإصدارات"
ملاحظة: إذا تم ضبطminSdkVersion
على 21 أو إصدار أحدث، سيتم تفعيل حزمة multidex تلقائيًا
ولن تحتاج إلى مكتبة multidex.
إذا تم ضبط minSdkVersion
على 20 أو أقل، يجب
استخدام
مكتبة multidex وإجراء
التعديلات التالية على مشروع تطبيقك:
-
عدِّل ملف
build.gradle
على مستوى الوحدة لأجل تفعيل حزمة multidex وإضافة مكتبة 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
class، غيِّرها لتوسيع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()
أو أي رمز برمجي آخر من خلال ميزة Reflection أو 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 للتعامل مع الحد الأقصى لمساحة التخزين في ذاكرة التخزين المؤقت (الخطأ 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") ... } } }
تحسين حِزم multidex في إصدارات التطوير
تتطلّب عملية إعداد حِزم متعددة بتنسيق 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
لكل صيغة لكي
لا تُوسِّع فئة MultiDexApplication
أو
تطلب MultiDex.install(this)
إلا الفئة الفرعية للمستوى 20 من واجهة برمجة التطبيقات والإصدارات الأقدم.
اختبار التطبيقات التي تستخدم ملفات 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); ... }