تفعيل ميزة "الاتصال المتعدد" للتطبيقات التي تتضمّن أكثر من 64 ألف طريقة

إذا كان تطبيقك يتضمّن 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). توضّح هذه الصفحة كيفية تجاوز هذا الحدّ من خلال تفعيل إعدادات التطبيق المعروفة باسم ملفات DEX المتعددة، والتي تسمح لتطبيقك بإنشاء ملفات DEX متعددة وقراءتها.

لمحة عن الحدّ الأقصى لعدد المراجع البالغ 64 ألفًا

تحتوي ملفات تطبيق Android ‏ (APK) على ملفات رمز بايت قابلة للتنفيذ في شكل ملفات Dalvik Executable ‏ (DEX)، والتي تحتوي على الرمز البرمجي المجمَّع المستخدَم لتشغيل تطبيقك. يحدّد مواصفات Dalvik Executable إجمالي عدد الطرق التي يمكن الإشارة إليها داخل ملف DEX واحد إلى 65,536، بما في ذلك طرق إطار عمل Android وطرق المكتبة والطرق في الرمز البرمجي الخاص بك.

في سياق علوم الكمبيوتر، يشير المصطلح كيلو أو K إلى 1024 (أو 2^10). بما أنّ 65,536 يساوي 64x1024، يُشار إلى هذا الحدّ باسم _الحدّ الأقصى للمراجع البالغ 64 ألفًا_.

إتاحة ملفات DEX المتعددة قبل الإصدار 5.0 من Android

تستخدم إصدارات النظام الأساسي السابقة لنظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) وقت تشغيل Dalvik لتنفيذ رمز التطبيق. يفرض Dalvik تلقائيًا على التطبيقات استخدام ملف رمز بايت واحد من نوع classes.dex لكل حزمة APK. لتجاوز هذا القيد، أضِف مكتبة ملفات DEX المتعددة إلى ملف 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 الإضافية والرمز البرمجي الذي تحتويه. للاطّلاع على الإصدارات الحالية لهذه المكتبة، يُرجى مراجعة إصدارات ملفات DEX المتعددة.

لمزيد من التفاصيل، يُرجى الاطّلاع على القسم الذي يتناول كيفية ضبط تطبيقك لاستخدام ملفات DEX متعددة.

إتاحة ملفات DEX المتعددة لنظام التشغيل Android 5.0 والإصدارات الأحدث

يستخدم نظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث بيئة تشغيل تُسمى ART تتيح تحميل ملفات DEX متعددة من ملفات APK. يُجري وقت تشغيل Android (ART) عملية تجميع مسبق عند تثبيت التطبيق، حيث يبحث عن ملفات classesN.dex ويجمعها في ملف OAT واحد لتنفيذه بواسطة جهاز Android. لذلك، إذا كان minSdkVersion يساوي 21 أو أعلى، يتم تفعيل ملفات DEX المتعددة تلقائيًا ولن تحتاج إلى مكتبة ملفات DEX المتعددة.

لمزيد من المعلومات حول وقت تشغيل Android 5.0، يُرجى الاطّلاع على وقت تشغيل Android ‏ (ART) وDalvik.

ملاحظة: عند تشغيل تطبيقك باستخدام "استوديو Android"، يتم تحسين عملية الإنشاء لتناسب أجهزة الاختبار التي يتم نشر التطبيق عليها. ويشمل ذلك تفعيل ملفات DEX المتعددة عندما تكون أجهزة الاختبار تعمل بالإصدار 5.0 من نظام التشغيل Android أو الإصدارات الأحدث. بما أنّ هذا التحسين لا يتم تطبيقه إلا عند نشر تطبيقك باستخدام "استوديو Android"، قد تحتاج إلى ضبط بنية الإصدار لملفات DEX المتعددة لتجنُّب حدّ الـ 64 ألف.

تجنُّب الحدّ الأقصى البالغ 64 ألفًا

قبل ضبط تطبيقك لتفعيل استخدام 64 ألف مرجع أو أكثر من مراجع الطرق، اتّخِذ خطوات لتقليل إجمالي عدد المراجع التي يستدعيها رمز تطبيقك، بما في ذلك الطرق المحدّدة من خلال رمز تطبيقك أو المكتبات المضمّنة.

يمكن أن تساعدك الاستراتيجيات التالية في تجنُّب بلوغ الحدّ الأقصى لعدد المراجع في ملفات DEX:

مراجعة التبعيات المباشرة والمتعدية لتطبيقك
عليك التفكير في ما إذا كانت قيمة أي تبعية كبيرة للمكتبة تدرجها في تطبيقك تفوق مقدار الرمز البرمجي الذي تتم إضافته إلى التطبيق. من الأنماط الشائعة ولكنها تمثّل مشكلة تضمين مكتبة كبيرة جدًا لأنّ بعض طرق الأدوات المساعدة كانت مفيدة. يمكن أن يساعدك تقليل تبعيات رمز تطبيقك في تجنُّب الحد الأقصى لعدد المراجع في ملف DEX.
إزالة الرموز غير المستخدَمة باستخدام R8
فعِّل تقليص حجم الرموز لتشغيل R8 لبُنى الإصدارات. فعِّل ميزة تقليص حجم الرموز البرمجية للتأكّد من عدم شحن رموز برمجية غير مستخدَمة مع حِزم APK. في حال ضبط ميزة تقليل حجم الرموز بشكل صحيح، يمكنها أيضًا إزالة الرموز والموارد غير المستخدَمة من التبعيات.

يمكن أن يساعدك استخدام هذه الأساليب في تقليل الحجم الإجمالي لملف APK وتجنُّب الحاجة إلى ملفات DEX متعددة في تطبيقك.

ضبط تطبيقك لاستخدام ملفات DEX متعددة

ملاحظة: إذا تم ضبط minSdkVersion على 21 أو إصدار أحدث، يتم تفعيل ملفات DEX المتعددة تلقائيًا ولن تحتاج إلى مكتبة ملفات DEX المتعددة.

إذا تم ضبط minSdkVersion على 20 أو أقل، عليك استخدام مكتبة multidex وإجراء التعديلات التالية على مشروع تطبيقك:

  1. عدِّل ملف 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")
    }
  2. استنادًا إلى ما إذا كنت تتجاوز فئة 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(). لن تتبع عملية تتبُّع ملفات DEX المتعددة هذه الطلبات، ما يؤدي إلى حدوث أخطاء ClassNotFoundException أو أخطاء في التحقّق بسبب تقسيم غير صحيح للفئات بين ملفات DEX.

الآن عند إنشاء تطبيقك، تنشئ أدوات إنشاء Android ملف DEX أساسيًا (classes.dex) وملفات DEX داعمة (classes2.dex وclasses3.dex وما إلى ذلك) حسب الحاجة. بعد ذلك، يحزم نظام الإنشاء جميع ملفات DEX في حزمة APK.

في وقت التشغيل، بدلاً من البحث في ملف classes.dex الرئيسي فقط، تستخدم واجهات برمجة التطبيقات لملفات DEX المتعددة أداة تحميل فئات خاصة للبحث في جميع ملفات DEX المتاحة عن الطرق التي تستخدمها.

القيود المفروضة على مكتبة ملفات DEX المتعددة

هناك بعض القيود المعروفة لمكتبة ملفات DEX المتعددة. عند دمج المكتبة في إعدادات تصميم تطبيقك، يُرجى مراعاة ما يلي:

  • إنّ تثبيت ملفات DEX أثناء بدء التشغيل على قسم بيانات الجهاز أمر معقّد ويمكن أن يؤدي إلى حدوث أخطاء "التطبيق لا يستجيب" (ANR) إذا كانت ملفات DEX الثانوية كبيرة. لتجنُّب هذه المشكلة، يمكنك تخفيض حجم الرموز لتقليل حجم ملفات DEX وإزالة الأجزاء غير المستخدَمة من الرموز البرمجية.
  • عند تشغيل التطبيق على إصدارات أقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، لا يكفي استخدام ملفات DEX متعددة لتجاوز حد 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

يستخدم ملف 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 الأساسي والفئات التي يمكن تضمينها في ملفات DEX الثانوية. يعني ذلك أنّ الإصدارات المتزايدة التي تستخدم ملفات DEX المتعددة تستغرق عادةً وقتًا أطول، وقد تؤدي إلى إبطاء عملية التطوير.

لتقليل أوقات الإنشاء المتزايدة، استخدِم التحويل إلى ملفات ‎.dex لإعادة استخدام مخرجات ملفات DEX المتعددة بين عمليات الإنشاء. تعتمد عملية التحويل إلى ملفات ‎.dex على تنسيق ART المتاح فقط على الإصدار 5.0 من Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث. إذا كنت تستخدم &quot;استوديو Android&quot;، تستخدم بيئة التطوير المتكاملة (IDE) تلقائيًا عملية التحويل إلى ملفات ‎.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" أو سطر الأوامر، يُرجى الاطّلاع على تحسين سرعة عملية الإنشاء. لمزيد من المعلومات حول استخدام تنويعات التصميم، يُرجى الاطّلاع على مقالة ضبط تنويعات التصميم.

ملاحظة: إذا كانت لديك تنويعات تصميم مختلفة لاحتياجات ملفات DEX المتعددة، يمكنك تقديم ملف بيان مختلف لكل تنويعة بحيث يغيّر الملف الخاص بالمستوى 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);
  ...
}