אזהרה: החל מאוגוסט 2021, חובה לפרסם את כל האפליקציות החדשות כ-App Bundles. אם אתם מפרסמים את האפליקציה ב-Google Play, עליכם ליצור קובץ Android App Bundle ולהעלות אותו. כשמפרסמים כמה חבילות APK, Google Play יוצרת אותן באופן אוטומטי ומציגה אותן עם אופטימיזציה בהתאם להגדרות המכשיר של כל משתמש, כך שהם מורידים רק את הקוד והמשאבים הנדרשים להפעלת האפליקציה. כדאי לפרסם כמה חבילות APK אם מפרסמים בחנות שלא תומכת בפורמט AAB. במקרה כזה, תצטרכו ליצור, לחתום ולנהל כל קובץ APK בעצמכם.
מומלץ ליצור קובץ APK יחיד שתומך בכל מכשירי היעד שלכם, כשהדבר אפשרי. עם זאת, יכול להיות שהקובץ יהיה גדול מאוד בגלל קבצים שתומכים במספר דחיסות מסך או ממשקי Application Binary (ABI). אחת הדרכים להקטין את גודל ה-APK היא ליצור חבילות APK מרובות שמכילות קבצים לצפיפות מסך ספציפית או ל-ABI ספציפי.
Gradle יכול ליצור קובצי APK נפרדים שמכילים רק קוד ומשאבים ספציפיים לכל צפיפות או ABI. בדף הזה מוסבר איך להגדיר את ה-build כך שייצור כמה חבילות APK. אם אתם צריכים ליצור גרסאות שונות של האפליקציה שלא מבוססות על צפיפות המסך או על ABI, תוכלו להשתמש במקום זאת בוריאנטים של גרסאות build.
הגדרת ה-build ליצירת כמה חבילות APK
כדי להגדיר את ה-build ליצירת כמה חבילות APK, מוסיפים בלוק
splits
לקובץ build.gradle
ברמת המודול. בתוך הבלוק splits
, מציינים בלוק density
שמציין איך רוצים ש-Gradle תיצור קובצי APK לפי צפיפות, או בלוק abi
שמציין איך רוצים ש-Gradle תיצור קובצי APK לפי ABI. אפשר לספק גם בלוקים של צפיפות וגם בלוקים של ABI, ומערכת ה-build תיצור קובץ APK לכל שילוב של צפיפות ו-ABI.
הגדרת כמה חבילות APK לדחיסות מסך שונות
כדי ליצור חבילות APK נפרדות לדחיסות מסך שונות, מוסיפים בלוק density
בתוך הבלוק splits
. בבלוק density
, מציינים רשימה של צפיפות המסך הרצויה ושל גדלי המסך התואמים. מומלץ להשתמש ברשימת גודלי המסך התואמים רק אם אתם צריכים להוסיף רכיבים ספציפיים של
<compatible-screens>
לכל מניפסט של APK.
אפשרויות ה-DSL הבאות של Gradle משמשות להגדרת כמה חבילות 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") } } }
למידע נוסף על התאמה אישית של גרסאות build שונות של האפליקציה לסוגים ספציפיים של מסכים ומכשירים, ראו הצהרה על תמיכה מוגבלת במסכים.
הגדרת כמה חבילות APK לממשקי ABI
כדי ליצור חבילות APK נפרדות לממשקי ABI שונים, מוסיפים בלוק abi
בתוך הבלוק splits
. בבלוק abi
, מספקים רשימה של ממשקי ABI הרצויים.
האפשרויות הבאות של Gradle DSL משמשות להגדרת כמה חבילות APK לכל ABI:
-
enable
עבור Groovy אוisEnable
עבור סקריפט Kotlin - אם מגדירים את האלמנט הזה ל-
true
, Gradle יוצר כמה חבילות APK על סמך ה-ABIs שתגדירו. ערך ברירת המחדל הואfalse
. -
exclude
-
מציינת רשימה של ABIs מופרדים בפסיקים שלא רוצים ש-Gradle תיצור להם חבילות APK נפרדות. משתמשים ב-
exclude
אם רוצים ליצור חבילות APK לרוב ממשקי ה-ABI, אבל צריך להחריג כמה ממשקי ABI שהאפליקציה לא תומכת בהם. -
reset()
-
ניקוי רשימת ברירת המחדל של ABI. משתמשים ב-
include
רק בשילוב עם הרכיבinclude
כדי לציין את ה-ABIs שרוצים להוסיף.קטע הקוד הבא מגדיר את רשימת ה-ABIs רק ל-
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
-
מציינת רשימה של ABIs שמופרדים בפסיקים שעבורם רוצים ש-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 יש שתי עמודות: Module ו-Active Build Variant, כפי שמוצג באיור 1.
איור 1. בחלונית Build Variants יש שתי עמודות לפרויקטים ללא קוד מקורי/C++.
הערך של Active Build Variant (גרסת build פעילה) של המודול קובע את גרסת ה-build שנפרסת ומוצגת בעורך. כדי לעבור בין הווריאנטים, לוחצים על התא Active Build Variant של המודול ובוחרים את הווריאנט הרצוי משדה הרשימה.
פרויקטים עם קוד מקורי/C++
בפרויקטים עם קוד מקורי/C++, בחלונית Build Variants יש שלוש עמודות: Module, Active Build Variant ו-Active ABI, כפי שמוצג באיור 2.
איור 2. בחלונית Build Variants נוספת העמודה Active ABI לפרויקטים עם קוד מקורי/C++.
הערך של Active Build Variant (גרסת build פעילה) לקובץ המודול קובע את גרסת ה-build שנפרסת ומוצגת בעורך. במודולים מקומיים, הערך של Active ABI קובע את ה-ABI שבו עושה שימוש העורך, אבל הוא לא משפיע על מה שנפרס.
כדי לשנות את סוג ה-build או את ה-ABI:
- לוחצים על התא של העמודה Active Build Variant או Active ABI.
- בוחרים את הווריאנט או את ה-ABI הרצוי בשדה הרשימה. סנכרון חדש יתבצע באופן אוטומטי.
שינוי של אחת מהעמודות באפליקציה או במודול הספרייה מחיל את השינוי על כל השורות התלויות.
הגדרת ניהול גרסאות
כברירת מחדל, כש-Gradle יוצר כמה חבילות APK, לכל אחת מהן יש את אותם פרטי גרסה, כפי שצוינו בקובץ build.gradle
או build.gradle.kts
ברמת המודול. חנות Google Play לא מאפשרת להעלות כמה קובצי APK של אותה אפליקציה עם אותם פרטי גרסה, לכן עליכם לוודא שלכל קובץ APK יש
versionCode
ייחודי לפני ההעלאה ל-Play Store.
אפשר להגדיר את קובץ build.gradle
ברמת המודול כדי לשנות את versionCode
לכל קובץ APK. אם יוצרים מיפוי שמקצה ערך מספרי ייחודי לכל ABI וצפיפות שמגדירים עבור כמה חבילות APK, אפשר לשנות את קוד הגרסה של הפלט בערך שמציג שילוב של קוד הגרסה שמוגדר בבלוק defaultConfig
או productFlavors
עם הערך המספרי שהוקצה לצפיפות או ל-ABI.
בדוגמה הבאה, ה-APK של ה-ABI x86
מקבל versionCode
של 2004, וה-ABI x86_64
מקבל versionCode
של 3004.
הקצאת קודי גרסה במרווחים גדולים, כמו 1,000, מאפשרת לכם להקצות מאוחר יותר קודי גרסה ייחודיים אם תצטרכו לעדכן את האפליקציה. לדוגמה, אם defaultConfig.versionCode
חוזר על עצמו עד 5 בעדכון מאוחר יותר, Gradle מקצה ל-x86
APK את הערך 2005 ול-x86_64
APK את הערך 3005.versionCode
טיפ: אם הגרסה שלכם כוללת APK אוניברסלי, צריך להקצות לו ערך versionCode
נמוך יותר מכל ערך אחר של חבילות ה-APK.
מכיוון שחנות Google Play מתקינה את גרסת האפליקציה שתואמת למכשיר היעד וגם את הגרסה עם הערך הגבוה ביותר של versionCode
, הקצאת ערך versionCode
נמוך יותר ל-APK האוניברסלי מבטיחה שחנות Google Play תנסה להתקין אחת מחבילות ה-APK שלכם לפני שתעבור ל-APK האוניברסלי. הקוד לדוגמה הבא מטפל בבעיה הזו על ידי כך שלא משנה את הערך שמוגדר כברירת מחדל של versionCode
בחבילת APK אוניברסלית.
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, לוחצים על Build (יצירת גרסה) > Build APK (יצירת קובץ APK) כדי ליצור את כל חבילות ה-APK של המודול שנבחר כרגע בחלונית Project. Gradle יוצר את חבילות ה-APK לכל צפיפות או ABI בתיקייה build/outputs/apk/
של הפרויקט.
Gradle יוצר קובץ APK לכל צפיפות או ABI שאתם מגדירים עבור כמה חבילות APK. אם מפעילים כמה חבילות APK גם לדחיסות וגם ל-ABI, Gradle יוצר חבילה לכל שילוב של דחיסות ו-ABI.
לדוגמה, קטע הקוד build.gradle
הבא מאפשר ליצור כמה קובצי APK לצפיפות mdpi
ו-hdpi
, וגם ל-ABI x86
ו-x86_64
:
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") } }
הפלט של ההגדרה לדוגמה כולל את 4 קובצי ה-APK הבאים:
app-hdpiX86-release.apk
: מכיל קוד ומשאבים לצפיפותhdpi
ול-ABI שלx86
.app-hdpiX86_64-release.apk
: מכיל קוד ומשאבים לצפיפותhdpi
ול-ABI שלx86_64
.app-mdpiX86-release.apk
: מכיל קוד ומשאבים לצפיפותmdpi
ול-ABI שלx86
.app-mdpiX86_64-release.apk
: מכיל קוד ומשאבים לצפיפותmdpi
ול-ABI שלx86_64
.
כשמפתחים כמה חבילות APK על סמך צפיפות המסך, Gradle יוצר תמיד חבילה אוניברסלית של APK שכוללת קוד ומשאבים לכל הצפיפויות, בנוסף לחבילות ה-APK לכל צפיפות.
כשמפתחים כמה קובצי APK על סמך ABI, Gradle יוצר קובץ APK שכולל קוד ומשאבים לכל ה-ABIs רק אם מציינים את הערך universalApk true
בבלוק splits.abi
בקובץ build.gradle
(ב-Groovy) או את הערך isUniversalApk = true
בבלוק splits.abi
בקובץ build.gradle.kts
(ב-Kotlin script).
הפורמט של שם קובץ ה-APK
כשמפתחים כמה חבילות APK, Gradle יוצר את שמות הקבצים שלהן לפי התבנית הבאה:
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
-
מציין את וריאנט ה-build שנוצר, למשל
debug
.
לדוגמה, כשיוצרים קובץ APK עם דחיסות מסך של mdpi
לגרסת ניפוי הבאגים של myApp, שם קובץ ה-APK הוא myApp-mdpi-debug.apk
. שם הקובץ של גרסת המהדורה של myApp שמוגדרת ליצירת כמה חבילות APK גם לדחיסות המסך mdpi
וגם ל-ABI x86
הוא myApp-mdpiX86-release.apk
.