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

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

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

يمكن لنظام 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
إذا ضبطت هذا العنصر على 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 لإزالة هذه الكثافات الثلاث من القائمة الافتراضية لجميع الكثافات.

Groovy

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) التي تريد إضافتها.

يضبط المقتطف التالي قائمة واجهات التطبيق الثنائية (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 لكل واجهة التطبيق الثنائية (ABI). تحتوي حزمة APK العامة على رمز وموارد لجميع واجهات التطبيق الثنائية (ABI) في حزمة APK واحدة. القيمة التلقائية هي false.

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

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

Groovy

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++ ، تحتوي لوحة Build Variants على اثنتين الأعمدة: الوحدة وActive Build الصيغة، كما هو موضّح في الشكل 1.


لوحة خيارات الإصدار الشكل 1. تحتوي لوحة إنشاء صيغ على عمودين للمشاريع التي ليس لها الرمز الأصلي/C++.

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

المشاريع التي تستخدم رموز برمجية أصلية/C++

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

الشكل 2. تضيف لوحة صيغ الإصدار عمود واجهة التطبيق الثنائية (ABI) النشطة من أجل باستخدام رمز أصلي/C++.

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

لتغيير نوع الإصدار أو واجهة التطبيق الثنائية (ABI):

  1. انقر على الخلية صيغة الإصدار النشط. أو واجهة التطبيق الثنائية (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 وx86_64 واجهة التطبيق الثنائية (ABI) على versionCode من 3004.

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

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

Groovy

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. لكل كثافة أو واجهة ABI في build/outputs/apk/ الخاصة بالمشروع الدليل.

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

على سبيل المثال، ما يلي يتيح مقتطف build.gradle إنشاء حِزم APK متعددة لـ mdpi و كثافات hdpi وأيضًا x86 وx86_64 واجهات التطبيق الثنائية (ABI):

Groovy

...
  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 متعددة استنادًا إلى لا تنشئ واجهة التطبيق الثنائية (ABI) سوى حزمة APK التي تتضمّن رموزًا وموارد لجميع التطبيقات. واجهات التطبيق الثنائية (ABI) في حال تحديد 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 لكل واجهة ABI ملفات APK، يستخدم Gradle universal باعتباره جزء ABI من حزمة APK العامة. .

buildvariant
تُحدِّد صيغة الإصدار التي يتم إنشاؤها، مثل debug.

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