إنشاء حِزم APK متعددة

ملاحظة: منذ آب (أغسطس) 2 2011، يجب نشر جميع التطبيقات الجديدة بتنسيق "مجموعة حزمات تطبيق". إذا كنت تنشر تطبيقك على Google Play، أنشئ مجموعة حزمات تطبيق Android وحمِّلها. عند إجراء ذلك، ينشئ Google Play تلقائيًا حِزم APK محسّنة ويعرضها لإعدادات جهاز كل مستخدم، حتى لا يتم تنزيل سوى الرمز البرمجي والموارد التي يحتاجها لتشغيل تطبيقك. يكون نشر حِزم APK متعددة مفيدًا إذا كنت تريد نشر تطبيقك على متجر لا يتيح استخدام تنسيق AAB. في هذه الحالة، عليك إنشاء كل حزمة APK وتوقيعها وإدارتها بنفسك.

على الرغم من أنّه من الأفضل إنشاء حزمة APK واحدة متوافقة مع جميع أجهزتك المستهدَفة كلما أمكن ذلك، قد يؤدي ذلك إلى إنشاء حزمة APK كبيرة جدًا بسبب الملفات المتوافقة مع قيم كثافة شاشة أو واجهات برمجة التطبيقات (ABI) متعددة. من الطرق التي يمكنك من خلالها تقليل حجم حزمة APK هي إنشاء حِزم APK متعددة تحتوي على ملفات لكثافة شاشة أو واجهات برمجة تطبيقات معيّنة.

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

ضبط الإصدار لإنشاء حِزم APK متعددة

لضبط عملية الإنشاء لإنشاء حِزم APK متعددة، أضِف كتلة splits إلى ملف build.gradle على مستوى الوحدة. ضمن العنصر splits، قدِّم عنصرًا density يحدِّد الطريقة التي تريد من Gradle بها إنشاء حِزم APK لكل كثافة أو عنصرًا abi يحدِّد الطريقة التي تريد من Gradle بها إنشاء حِزم APK لكل ABI. يمكنك تقديم كل من كتل كثافة الشاشة وABI، وينشئ نظام الإنشاء حزمة APK لكل مجموعة من كثافة الشاشة وABI.

ضبط حِزم APK متعددة لكثافة الشاشة

لإنشاء حِزم APK منفصلة لكثافات شاشة مختلفة، أضِف كتلة density داخل كتلة splits. في العنصر density، قدِّم قائمة بكثافات الشاشة المطلوبة وأحجام الشاشة المتوافقة. لا تستخدِم قائمة أحجام الشاشات المتوافقة إلا إذا كنت بحاجة إلى عناصر <compatible-screens> معيّنة في بيان كل ملف APK.

تُستخدَم خيارات Gradle DSL التالية لضبط حِزم APK متعددة لكثافات الشاشة التالية:

enable لملف Groovy، وisEnable لملف Kotlin script
في حال ضبط هذا العنصر على true، ينشئ Gradle حِزم APK متعددة استنادًا إلى كثافات الشاشة التي تحدّدها. القيمة التلقائية هي false.
exclude
يحدِّد قائمة مفصولة بفواصل بكثافات لا تريد من Gradle إنشاء حِزم APK منفصلة لها. استخدِم exclude إذا أردت إنشاء حِزم APK لمعظم كثافات الشاشة، ولكنك بحاجة إلى استبعاد بعض القيم التي لا يتوافق معها تطبيقك.
reset()

تمحو هذه القيمة القائمة التلقائية لكثافة الشاشة. لا تستخدِم هذا العنصر إلّا مع العنصر include لتحديد الكثافات التي تريد إضافتها.

يضبط المقتطف التالي قائمة الكثافات على ldpi وxxhdpi فقط من خلال استدعاء reset() لمحو القائمة، ثم استخدام include:

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
لتحديد قائمة مفصولة بفواصل بكثافات الشاشة التي تريد أن ينشئ Gradle حِزم APK لها لا تستخدِم هذه السمة إلا مع reset() لتحديد قائمة دقيقة للكثافات.
compatibleScreens

تحدّد هذه السمة قائمة مفصولة بفواصل بأحجام الشاشات المتوافقة. يؤدي ذلك إلى إدراج عقدة <compatible-screens> مطابقة في بيان كل حزمة APK.

يقدّم هذا الإعداد طريقة ملائمة لإدارة كلّ من كثافة الشاشة وأحجامها في قسم build.gradle نفسه. ومع ذلك، يمكن أن يؤدي استخدام <compatible-screens> إلى الحد من أنواع أجهزة تطبيقك. للاطّلاع على طرق بديلة لإتاحة استخدام أحجام شاشة مختلفة، يمكنك الاطّلاع على نظرة عامة على التوافق مع الشاشات.

بما أنّ كل حزمة APK تستند إلى كثافة الشاشة، فهي تتضمّن علامة <compatible-screens> تتضمّن قيودًا محدّدة حول أنواع الشاشات التي تتوافق معها حزمة APK، حتى إذا كنت تنشر عدة حِزم APK، لا تتطابق بعض الأجهزة الجديدة مع فلاتر حِزم APK المتعددة. وبناءً على ذلك، ينشئ Gradle دائمًا حزمة APK عامة إضافية تحتوي على مواد عرض لجميع كثافات الشاشة ولا تتضمّن علامة <compatible-screens>. انشر حزمة APK العامة هذه مع حِزم APK المخصّصة لكل كثافة لتقديم بديل لأجهزة التي لا تتطابق مع حِزم APK التي تتضمّن علامة <compatible-screens>.

ينشئ المثال التالي حزمة APK منفصلة لكل كثافة شاشة باستثناء ldpi وxxhdpi وxxxhdpi. ويتم ذلك باستخدام exclude لإزالة هذه الكثافات الثلاث من القائمة التلقائية لجميع الكثافات.

رائع

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
    ...
    splits {

        // Configures multiple APKs based on screen density.
        density {

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

لمزيد من التفاصيل حول تخصيص إصدارات مختلفة من تطبيقك لأنواع شاشات وأجهزة معيّنة، يُرجى الاطّلاع على الإفصاح عن توافق الشاشة المحدود.

ضبط حِزم APK متعددة لواجهات ABI

لإنشاء حِزم APK منفصلة لأنواع ABI مختلفة، أضِف كتلة abi داخل كتلة splits. في القسم abi، قدِّم قائمة بملفَّات ABI المطلوبة.

تُستخدَم خيارات Gradle DSL التالية لضبط حِزم APK متعددة لكل واجهة ABI:

enable لـ Groovy أو isEnable لنص Kotlin برمجي
في حال ضبط هذا العنصر على true، ينشئ Gradle عدة حِزم APK استنادًا إلى معرّفات ABI التي تحدّدها. تكون القيمة التلقائية false.
exclude
يحدِّد قائمة مفصولة بفواصل بأسماء معرّفات ABI التي لا تريد من Gradle إنشاء حِزم APK منفصلة لها. استخدِم exclude إذا كنت تريد إنشاء حِزم APK لمعظم واجهات ABI ولكنك بحاجة إلى استبعاد بعض واجهات ABI التي لا يتوافق معها تطبيقك.
reset()

تمحو هذه القيمة القائمة التلقائية لعناوين ABI. لا تستخدِم هذا العنصر إلا مع العنصر include لتحديد واجهات برمجة التطبيقات التي تريد إضافتها.

يضبط المقتطف التالي قائمة واجهات ABI على x86 فقط x86_64 من خلال استدعاء reset() لمحو القائمة، ثم استخدام include:

reset()                 // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
يحدِّد قائمة مفصولة بفواصل لواجهات ABI التي تريد أن ينشئ Gradle حِزم APK لها. لا تستخدِم هذه السمة إلا مع reset() لتحديد قائمة محددة لواجهات ABI.
universalApk لـ Groovy أو isUniversalApk لملف برمجي Kotlin

إذا كان الخيار true، ينشئ Gradle حزمة APK عامة بالإضافة إلى حِزم APK لكل واجهة برمجة تطبيقات ثنائية. تحتوي حزمة APK العامة على رمز وموارد لجميع واجهات برمجة التطبيقات (ABI) في حزمة APK واحدة. تكون القيمة التلقائية false.

يُرجى العلم أنّ هذا الخيار لا يتوفّر إلا في مجموعة splits.abi. عند إنشاء حِزم APK متعددة بناءً على كثافة الشاشة، ينشئ Gradle دائمًا حزمة APK عامة تحتوي على الرموز البرمجية والموارد لجميع كثافات الشاشة.

ينشئ المثال التالي حزمة APK منفصلة لكل بنية أساسية للتطبيق: x86 وx86_64. ويتم ذلك باستخدام reset() للبدء بقائمة فارغة من واجهات ABI، متبوعة بـ include مع قائمة واجهات ABI التي تحصل كل منها على حزمة APK.

رائع

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include "x86", "x86_64"

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include("x86", "x86_64")

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

للحصول على قائمة بواجهات ABI المتوافقة، يُرجى الاطّلاع على واجهات ABI المتوافقة.

المشاريع التي لا تتضمّن رمزًا أصليًا أو رمز C++

بالنسبة إلى المشاريع التي لا تتضمّن رمزًا برمجيًا أصليًا أو رمزًا لبرنامج C++، تحتوي لوحة صيغة الإصدار على عمودَين: الوحدة وصيغة الإصدار النشطة، كما هو موضّح في الشكل 1.

لوحة &quot;صيغ التصميم&quot;
الشكل 1. تحتوي لوحة نُسخ الإصدار على عمودَين للمشاريع التي لا تحتوي على رمز برمجي برمجي/C++.

تحدِّد قيمة صيغة الإصدار النشطة لملف الوحدة نوع الإصدار الذي يتم نشره وظهوره في المحرِّر. للتبديل بين الصيغ، انقر على خلية صيغة الإصدار النشطة لأحد الوحدات واختَر الصيغة المطلوبة من حقل القائمة.

المشاريع التي تحتوي على رمز برمجي أصلي/C++

بالنسبة إلى المشاريع التي تحتوي على رمز برمجي أصلي/C++، تحتوي لوحة صيغة الإصدار على ثلاثة عمودين: الوحدة وصيغة الإصدار النشطة وActive ABI، كما هو موضّح في الشكل 2.

الشكل 2: تضيف لوحة نُسخ الإصدار عمود Active ABI لمشاريع التي تحتوي على رمز برمجي أصلي/C++.

تحدِّد قيمة صيغة الإصدار النشطة للوحدة صيغة الإصدار التي يتم نشرها وتكون مرئية في المحرِّر. بالنسبة إلى الوحدات الأصلية، تحدِّد قيمة Active ABI معرّف ABI الذي يستخدمه المحرِّر، ولكنّها لا تؤثّر في ما يتم نشره.

لتغيير نوع الإصدار أو ABI:

  1. انقر على خلية عمود Active Build Variant (صيغة الإصدار النشطة) أو Active ABI (مجموعة أدوات ربط التطبيقات النشطة).
  2. اختَر الصيغة أو ABI المطلوبَين من حقل القائمة. يتم تشغيل مزامنة جديدة تلقائيًا.

يؤدي تغيير أيّ من عمودَي تطبيق أو وحدة مكتبة إلى تطبيق التغيير على جميع الصفوف التابعة.

ضبط نظام الإصدار

بشكلٍ تلقائي، عندما ينشئ Gradle حِزم APK متعددة، تحتوي كل حزمة APK على معلومات الإصدار نفسها، كما هو محدّد في ملفَّي build.gradle أو build.gradle.kts على مستوى الوحدة. بما أنّ "متجر Google Play" لا يسمح بتحميل حِزم APK متعددة للتطبيق نفسه تحتوي كلّها على معلومات الإصدار نفسها، عليك التأكّد من أنّ كل حزمة APK تحتوي على versionCode فريد قبل تحميلها إلى "متجر Play".

يمكنك ضبط ملف build.gradle على مستوى الوحدة ل إلغاء versionCode لكل ملف APK. من خلال إنشاء تعيين يحدّد قيمة رقمية فريدة لكل ABI وكثافة شاشة تضبط ملفات APK متعددة لها، يمكنك إلغاء رمز إصدار الإخراج باستخدام قيمة تجمع رمز الإصدار المحدّد ضمن رمز defaultConfig أو productFlavors مع القيمة الرقمية المحدّدة للكثافة أو ABI.

في المثال التالي، يحصل حِزمة APK لملف تعريف ABI‏x86 على versionCode‏ 2004 ويحصل ملف تعريف ABI‏x86_64 على versionCode‏ 3004.

يتيح لك منح رموز الإصدارات بزيادة كبيرة، مثل 1000، منح رموز إصدار فريدة في وقت لاحق إذا كنت بحاجة إلى تحديث تطبيقك. على سبيل المثال، إذا تكررت عملية defaultConfig.versionCode إلى 5 في تحديث لاحق، يمنح Gradle versionCode بقيمة 2005 لملف APKx86 و3005 لملف APKx86_64.

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

رائع

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code doesn't override the version code for universal APKs.
    // However, because you want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the
      // version code for only the output APK, not for the variant itself. Skipping
      // this step causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)

// For per-density APKs, create a similar map:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" to 3)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code doesn't override the version code for universal APKs.
            // However, because you want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

للحصول على المزيد من الأمثلة على مخطّطات رموز الإصدارات البديلة، يُرجى الاطّلاع على منح رموز الإصدارات.

إنشاء حِزم APK متعددة

بعد ضبط ملف build.gradle أو build.gradle.kts على مستوى الوحدة لإنشاء حِزم APK متعددة، انقر على إنشاء > إنشاء حِزم APK لإنشاء جميع حِزم APK للوحدة التي تم اختيارها حاليًا في لوحة المشروع. ينشئ Gradle حِزم APK لكل كثافة أو معرّف بنية التطبيق الأساسية في دليل build/outputs/apk/ للمشروع.

ينشئ Gradle حزمة APK لكل كثافة شاشة أو واجهة ABI تضبط حِزم APK متعددة لها. في حال تفعيل حِزم APK متعددة لكل من كثافات الشاشة وواجهات ABI، ينشئ Gradle حزمة APK لكل مجموعة من كثافة الشاشة وواجهة ABI.

على سبيل المثال، يتيح المقتطف التالي build.gradle إنشاء حِزم APK متعددة لكثافتَي mdpi و hdpi، بالإضافة إلى معرّفات ABI لكل من x86 وx86_64:

رائع

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

Kotlin

...
  splits {
    density {
      isEnable = true
      reset()
      include("mdpi", "hdpi")
    }
    abi {
      isEnable = true
      reset()
      include("x86", "x86_64")
    }
  }

تتضمّن النتيجة من مثال الضبط ملفات APK الأربعة التالية:

  • app-hdpiX86-release.apk: يحتوي على رمز وموارد لكثافة hdpi وx86 ABI.
  • app-hdpiX86_64-release.apk: يحتوي على رمز وموارد لكثافة hdpi وx86_64 ABI.
  • app-mdpiX86-release.apk: يحتوي على رمز وموارد لكثافة mdpi وx86 ABI.
  • app-mdpiX86_64-release.apk: يحتوي على رمز وموارد لكثافة mdpi وx86_64 ABI.

عند إنشاء حِزم APK متعددة استنادًا إلى كثافة الشاشة، ينشئ Gradle دائمًا حزمة APK عامة تتضمّن الرموز البرمجية والموارد لجميع كثافات الشاشة، بالإضافة إلى حِزم APK لكل كثافة شاشة.

عند إنشاء حِزم APK متعددة استنادًا إلى IDE، لا ينشئ Gradle حزمة APK تتضمّن الرمز البرمجي والموارد لجميع IDE إلا إذا حدّدت universalApk true في مجموعة splits.abi في ملف build.gradle (للغة Groovy) أو isUniversalApk = true في مجموعة splits.abi في ملف build.gradle.kts (لنص Kotlin).

تنسيق اسم ملف APK

عند إنشاء حِزم APK متعددة، ينشئ Gradle أسماء حِزم APK باستخدام الطريِق التالية:

modulename-screendensityABI-buildvariant.apk

مكونات المخطّط هي:

modulename
لتحديد اسم الوحدة التي يتم إنشاؤها.
screendensity
في حال تفعيل حِزم APK متعددة لكثافة الشاشة، يُستخدَم هذا المَعلمة لتحديد كثافة الشاشة لملف APK، مثل mdpi.
ABI

في حال تفعيل حِزم APK متعددة لواجهة ABI، حدِّد واجهة ABI لحزمة APK، مثل x86.

في حال تفعيل حِزم APK متعددة لكل من كثافة الشاشة وABI، تُجمِّع Gradle اسم الكثافة مع اسم ABI، على سبيل المثال mdpiX86. في حال تفعيل universalApk لحِزم APK الخاصة بكل واجهة برمجة تطبيقات (ABI)، يستخدم Gradle universal كجزء من واجهة ABI في اسم حِزمة APK العامة.

buildvariant
يحدِّد هذا العنصر نوع الإصدار الذي يتم إنشاؤه، مثل debug.

على سبيل المثال، عند إنشاء حزمة APK لكثافة شاشة mdpi لإصدار debugging من myApp، يكون اسم ملف APK هو myApp-mdpi-debug.apk. إصدار تطبيق myApp الذي تم إعداده لإنشاء حِزم APK متعددة لكل من كثافة الشاشة mdpi وملف ABI x86 يحتوي على اسم ملف APK هو myApp-mdpiX86-release.apk.