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

حوالي 64 ألف مرجع

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

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

دعم Multidex قبل الإصدار Android 5.0

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

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

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

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

تجنَّب الوصول إلى عدد أكبر من 64 ألف

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

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

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

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

ضبط تطبيقك لاستخدام ميزة Multidex

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

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

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

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

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

حدود مكتبة الوسائط المتعددة

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

  • إنّ تثبيت ملفات DEX أثناء بدء التشغيل على قسم بيانات الجهاز معقّد، ويمكن أن يؤدي إلى حدوث أخطاء "التطبيق لا يستجيب" (ANR) إذا كانت ملفات DEX الثانوية كبيرة. لتجنُّب هذه المشكلة، يمكنك تفعيل تخفيض حجم الرموز لتقليل حجم ملفات DEX وإزالة الأجزاء غير المستخدَمة من الرمز.
  • وعند تشغيل هذه التطبيقات على إصدارات أقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، لا يكفي استخدام Multidex للتحايل على حدّ الحجم الخطي (المشكلة 37008143). وتمت زيادة هذا الحدّ في Android 4.0 (المستوى 14 من واجهة برمجة التطبيقات)، ولكن لم يؤدّي ذلك إلى حلّ المشكلة بالكامل.

    في الإصدارات الأقل من Android 4.0، قد تصل إلى الحدّ الأقصى المسموح به للمحور الخطي قبل الوصول إلى حد فهرس DEX. وبالتالي، إذا كنت تستهدف مستويات أقل من 14 لواجهة برمجة التطبيقات، عليك إجراء اختبار دقيق على هذه الإصدارات من النظام الأساسي، لأنّ تطبيقك قد يواجه مشاكل عند بدء التشغيل أو عند تحميل مجموعات محدّدة من الصفوف.

    ويمكن أن يؤدي تقليص الرموز إلى الحدّ من هذه المشاكل أو يمكن حلّها.

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

للحدّ من فترات الإنشاء المتزايدة والإضافية، يمكنك استخدام الفهرسة المُسبَقة لإعادة استخدام نتائج تصدير البيانات المتعدّدة بين الإصدارات. وتعتمد عملية الإعداد المسبق على تنسيق ART المتوفِّر فقط على نظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث. إذا كنت تستخدم "استوديو Android"، يستخدم IDE تلقائيًا ميزة الفهرسة المُسبَقة عند نشر تطبيقك على جهاز يعمل بالإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث. مع ذلك، إذا كنت تشغّل إصدارات Gradle من سطر الأوامر، يجب ضبط minSdkVersion على 21 أو أعلى لتفعيل الفهرسة المسبقة.

للحفاظ على إعدادات الإصدار العلني، يمكنك إنشاء نسختَين من تطبيقك باستخدام نكهات المنتجات، إحداهما تحمل نكهة تطوير والأخرى تتضمّن نكهة إصدار، مع قيم مختلفة للسمة 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" أو سطر الأوامر، يُرجى الاطّلاع على مقالة تحسين سرعة الإصدار. لمزيد من المعلومات عن استخدام صيغ الإصدار، راجِع ضبط صيغ الإصدار.

ملاحظة: إذا كانت لديك صيغ إصدار مختلفة لاحتياجات مختلفة متعلّقة بامتداد الوسائط المتعددة، يمكنك تقديم ملف بيان مختلف لكل صيغة، بحيث لا يغيّر اسم العلامة <application> سوى ملف المستوى 20 من واجهة برمجة التطبيقات والمستويات الأدنى. يمكنك أيضًا إنشاء فئة فرعية Application مختلفة لكل خيار منتج، بحيث تقتصر الفئة الفرعية للمستوى 20 لواجهة برمجة التطبيقات والأدنى على إضافة الفئة MultiDexApplication أو الطلبات MultiDex.install(this).

اختبار تطبيقات Multidex

عند كتابة اختبارات قياس حالة تطبيقات 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);
  ...
}