تفعيل ميزة "الاتصال المتعدد" للتطبيقات التي تتضمّن أكثر من 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، يشار إلى هذا الحد _64 ألف حد أقصى لعدد الملفات المرجعية_.

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

تستخدم إصدارات النظام الأساسي الأقدم من Android 5.0 (المستوى 21 لواجهة برمجة التطبيقات) وقت تشغيل Dalvik لتنفيذ رمز التطبيق. يحصر Dalvik التطبيقات تلقائيًا في استخدام ملف bytecode واحد فقط classes.dex لكل حزمة APK. للتغلب على هذا القيد، أضِف مكتبة multidex إلى ملف build.gradle أو build.gradle.kts على مستوى الوحدة:

رائعKotlin
dependencies {
    def multidex_version = "2.0.1"
    implementation "androidx.multidex:multidex:$multidex_version"
}
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. الفن التجميع المسبق للبيانات في وقت تثبيت التطبيق، ويبحث عن classesN.dex ملف وتجميعها في ملف واحد ملف OAT لـ الجهاز الذي يعمل بنظام التشغيل Android. لذلك، إذا كانت minSdkVersion إذا كان عمر 21 عامًا أو أكثر، يتم تفعيل ميزة Multidex تلقائيًا ولا تحتاج إلى مكتبة Multidex.

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

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

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

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

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

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

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

ضبط تطبيقك للاستفادة من ميزة "متعدد الإصدارات"

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

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

  1. عدِّل ملف build.gradle على مستوى الوحدة لأجل تفعيل حزمة multidex وإضافة مكتبة multidex كعنصر تابع، كما هو موضّح هنا:

    رائعKotlin
    android {
        defaultConfig {
            ...
            minSdkVersion 15 
            targetSdkVersion 33
            multiDexEnabled true
        }
        ...
    }
    
    dependencies {
        implementation "androidx.multidex:multidex:2.0.1"
    }
    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 كما يلي:

      KotlinJava
      class MyApplication : MultiDexApplication() {...}
      public class MyApplication extends MultiDexApplication { ... }
    • في حال إلغاء Application الفئة ولكن لا يمكن تغيير الفئة الأساسية، فعندئذ بدلاً من ذلك، إلغاء طريقة attachBaseContext() والاتصال بـ MultiDex.install(this) للتفعيل :multidex:

      KotlinJava
      class MyApplication : SomeOtherApplication() {
      
          override fun attachBaseContext(base: Context) {
              super.attachBaseContext(base)
              MultiDex.install(this)
          }
      }
      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(base);
           MultiDex.install(this);
        }
      }

      تحذير: لا تنفِّذ MultiDex.install() أو أي رمز برمجي آخر من خلال ميزة Reflection أو JNI قبل اكتمال MultiDex.install(). سيساعدك تتبع الارتباطات المتعددة عدم متابعة هذه المكالمات، ما يؤدي إلى حدوث ClassNotFoundException أو التحقّق من الأخطاء بسبب تقسيم فئة غير صالح بين ملفات DEX.

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

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

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

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

  • إنّ تثبيت ملفات 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.

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

يمكنك بعد ذلك الإفصاح عن هذا الملف لنوع الإصدار على النحو التالي:

رائعKotlin
android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
            ...
        }
    }
}
android {
    buildTypes {
        getByName("release") {
            multiDexKeepProguard = file("multidex-config.pro")
            ...
        }
    }
}

تحسين حِزم multidex في إصدارات التطوير

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

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

للحفاظ على إعدادات حسابك، يمكنك إنشاء إصدارين من تطبيقك استخدام نكهات المنتج: إصدار واحد لنكهة التطوير ونسخة واحدة بنكهة الإصدار، مع القيم المختلفة لـ minSdkVersion، كما هو موضح:

رائعKotlin
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"
}
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 لكل صيغة لكي لا تُوسِّع فئة MultiDexApplication أو تطلب MultiDex.install(this) إلا الفئة الفرعية للمستوى 20 من واجهة برمجة التطبيقات والإصدارات الأقدم.

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

عند كتابة اختبارات أداة القياس لتطبيقات حِزم متعددة، لن تكون هناك حاجة إلى ضبط أي إعدادات إضافية إذا كنت تستخدم أداة قياس MonitoringInstrumentation أو AndroidJUnitRunner . إذا كنت تستخدم Instrumentation آخر، عليك إلغاء طريقة onCreate() باستخدام الرمز التالي:

KotlinJava
fun onCreate(arguments: Bundle) {
  MultiDex.install(targetContext)
  super.onCreate(arguments)
  ...
}
public void onCreate(Bundle arguments) {
  MultiDex.install(getTargetContext());
  super.onCreate(arguments);
  ...
}