تفعيل ميزة "الاتصال المتعدد" للتطبيقات التي تتضمّن أكثر من 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 واحد إلى 65,536، بما في ذلك طرق إطار عمل Android وطرق الترميز الخاصة بك.

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

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

للحصول على مزيد من التفاصيل، يمكنك الاطلاع على القسم حول كيفية ضبط تطبيقك لاستخدام ميزة الوسائط المتعددة.

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

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

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

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

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

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

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

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

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

إعداد تطبيقك للاستخدام المتعدد

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

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

  1. عليك تعديل ملف build.gradle على مستوى الوحدة لتفعيل ميزة التخصيص المتعدد وتضيف مكتبة الوسائط المتعددة كتبعية، كما هو موضّح هنا:

    رائع

    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) لتفعيل ميزة تعدد الوسائط:

      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 الرئيسي، تستخدم واجهات برمجة التطبيقات المتعددة القنوات أداة تحميل فئة خاصة للبحث في جميع ملفات DEX المتاحة عن طُرقك.

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

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

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

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

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

اختبار تطبيقات الترميز المتعدد

عند كتابة اختبارات قياس الأداء للتطبيقات المتعددة الوظائف، لا يُطلب منك ضبط أي إعدادات إضافية إذا كنت تستخدم 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);
  ...
}