تنبيه: منذ آب (أغسطس) 2021، يجب نشر جميع التطبيقات الجديدة بتنسيق حزمات تطبيق. في حال نشر تطبيقك على Google Play، عليك إنشاء مجموعة حزمات تطبيق Android وتحميلها. عند إجراء ذلك، سينشئ Google Play تلقائيًا حِزم APK محسّنة ويعرضها وفقًا لإعدادات جهاز كل مستخدم، لذلك يتم تنزيل الرموز البرمجية والموارد التي يحتاجونها لتشغيل تطبيقك. ومن المفيد نشر عدة حِزم APK في حال النشر على متجر لا يتوافق مع تنسيق AAB. وفي هذه الحالة، يجب إنشاء كل حزمة APK وتوقيعها وإدارتها بنفسك.
على الرغم من أنّه من الأفضل إنشاء ملف APK واحد للتوافق مع جميع الأجهزة المستهدفة كلما أمكن، قد يؤدي ذلك إلى إنشاء حزمة APK كبيرة جدًا بسبب الملفات التي تتوافق مع العديد من كثافات الشاشة أو واجهات التطبيقات الثنائية (ABIs). يمكنك تقليل حجم حزمة 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
للنص الرائع و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
لإزالة تلك الكثافات الثلاث من القائمة التلقائية لجميع الكثافات.
رائع
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.
رائع
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.
الشكل 1. تحتوي لوحة إنشاء المتغيرات على عمودين للمشروعات التي لا تحتوي على رمز أصلي/C++.
وتحدِّد قيمة صيغة الإصدار النشط للوحدة صيغة الإصدار التي تم نشرها ورؤيتها في المحرِّر. للتبديل بين الصِيَغ، انقر على الخلية صيغة الإصدار النشط لإحدى الوحدات واختَر الصيغة المطلوبة من حقل القائمة.
المشاريع باستخدام الرموز البرمجية الأصلية/لغة C++
بالنسبة إلى المشاريع التي تتضمّن رموزًا برمجية أصلية/C++ ، تتضمّن لوحة إنشاء الصيغ ثلاثة أعمدة: وحدة، وصيغة نشطة للإصدار، وواجهة ABI النشطة، كما هو موضّح في الشكل 2.
الشكل 2. تضيف لوحة إنشاء صيغ عمود Active ABI للمشروعات ذات الرموز الأصلية/C++.
إنّ قيمة صيغة الإصدار النشط للوحدة تحدّد صيغة الإصدار التي تم نشرها وتكون مرئية في المحرّر. في الوحدات الأصلية، تحدِّد قيمة Active ABI واجهة التطبيق الثنائية (ABI) التي يستخدمها المحرّر، ولكنها لا تؤثر في التطبيقات التي يتم نشرها.
لتغيير نوع الإصدار أو واجهة التطبيق الثنائية (ABI):
- انقر على الخلية في عمود صيغة الإصدار النشط أو واجهة ABI النشطة.
- اختَر الصيغة المطلوبة أو واجهة 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 x86
ABI
على 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
التلقائي الخاص بحزمة 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
لكل كثافة أو كل واجهة ABI في دليل build/outputs/apk/
للمشروع.
تنشئ Gradle ملف APK لكل كثافة أو واجهة ABI يتم إعداد حِزم APK متعددة لها. في حال تفعيل عدة حِزم APK لكل من قيم الكثافة وواجهات ABI، تنشئ أداة Gradle حزمة APK لكل مجموعة كثافة وواجهة ABI.
على سبيل المثال، يتيح مقتطف build.gradle
التالي إنشاء حِزم APK متعدّدة لكثافة mdpi
وhdpi
، بالإضافة إلى x86
وx86_64
ABI:
رائع
... 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)، لا ينشئ Gradle سوى حزمة APK تتضمّن رموزًا وموارد لجميع واجهات ABI في حال تحديد universalApk true
في المجموعة splits.abi
في ملف build.gradle
(للتطبيق Groovy) أو في المجموعة splits.abi
في ملف build.gradle.kts
الخاص بك
(لنص برمجي بلغة Kotlin).isUniversalApk = true
تنسيق اسم ملف 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)، تستخدم Gradleuniversal
كجزء من واجهة التطبيق الثنائية (ABI) من اسم ملف APK العام. -
buildvariant
-
تحدّد هذه السمة صيغة الإصدار الذي يتم إنشاؤه، مثل
debug
.
على سبيل المثال، عند إنشاء حزمة APK لكثافة الشاشة mdpi
لإصدار تصحيح الأخطاء من myApp، يكون اسم ملف APK هو
myApp-mdpi-debug.apk
. إصدار الإصدار من myApp الذي تم إعداده لإنشاء عدة حِزم APK لكل من
كثافة شاشة mdpi
وواجهة ABI لـ x86
تحتوي على اسم ملف APK هو myApp-mdpiX86-release.apk
.