למרות שעדיף ליצור קובץ APK אחד שיתמוך בכל מכשירי היעד, לפעמים זה לא אפשרי. במקרים כאלה, יכול להיות שקובץ ה-APK יהיה גדול מאוד בגלל קבצים שתומכים בכמה ממשקי Application Binary Interface (ABI). אחת הדרכים להקטין את גודל ה-APK היא ליצור כמה קובצי APK שמכילים קבצים לממשקי ABI ספציפיים.
Gradle יכול ליצור קובצי APK נפרדים שמכילים רק קוד ומשאבים ספציפיים לכל ABI. בדף הזה מוסבר איך להגדיר את ה-build כדי ליצור כמה קובצי APK. אם אתם צריכים ליצור גרסאות שונות של האפליקציה שלא מבוססות על ממשק ABI, השתמשו בוריאנט build במקום זאת.
הגדרת ה-build לכמה חבילות APK
כדי להגדיר את ה-build לכמה חבילות APK, מוסיפים בלוק splits לקובץ build.gradle ברמת המודול. בתוך הבלוק splits, מוסיפים בלוק abi שמציין איך Gradle צריך ליצור קובצי APK לכל ABI.
הגדרת כמה חבילות APK לממשקי ABI
כדי ליצור קובצי APK נפרדים לממשקי ABI שונים, מוסיפים בלוק abi בתוך הבלוק splits. בבלוק abi, מספקים רשימה של ממשקי ה-ABI שבחרתם.
אפשרויות ה-DSL הבאות של Gradle משמשות להגדרת כמה חבילות 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.
בדוגמה הבאה נוצר קובץ 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 הנתמכים.
פרויקטים ללא קוד נייטיב או קוד C++
בפרויקטים בלי קוד נייטיב או קוד C++, בחלונית Build Variants יש שתי עמודות: Module ו-Active Build Variant, כמו שמוצג באיור 1.
הערך של Active Build Variant של המודול קובע את וריאנט ה-build שנפרס ומוצג בכלי העריכה. כדי לעבור בין וריאנטים, לוחצים על התא Active Build Variant של מודול ובוחרים את וריאנט ה-build הרצוי משדה הרשימה.
פרויקטים עם קוד נייטיב או קוד C++
בפרויקטים עם קוד נייטיב/C++, בחלונית Build Variants יש שלוש עמודות: Module, Active Build Variant ו-Active ABI, כמו שמוצג באיור 2.
הערך של Active Build Variant של המודול קובע את וריאנט ה-build שנפרס ומוצג בכלי העריכה. במודולים מקוריים, הערך של Active ABI קובע את ה-ABI שבו משתמש העורך, אבל לא משפיע על מה שנפרס.
כדי לשנות את סוג ה-build או את ה-ABI:
- לוחצים על התא בעמודה וריאנט build פעיל או ABI פעיל.
- בוחרים את הווריאנט או ה-ABI הרצויים משדה הרשימה. סנכרון חדש פועל באופן אוטומטי.
אם משנים את אחת מהעמודות של אפליקציה או מודול ספרייה, השינוי חל על כל השורות התלויות.
הגדרת ניהול גרסאות
כברירת מחדל, כש-Gradle יוצר כמה קובצי APK, לכל קובץ APK יש את אותם פרטי גרסה, כפי שצוין בקובץ build.gradle או build.gradle.kts ברמת המודול. בחנות Google Play אי אפשר להעלות כמה קובצי APK לאותה אפליקציה עם פרטי גרסה זהים. לכן, לפני שמעלים את קובצי ה-APK לחנות Play, צריך לוודא שלכל קובץ APK יש versionCode ייחודי.
אפשר להגדיר את קובץ 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 מקצה versionCode של 2005 ל-APK של x86 ו-3005 ל-APK של x86_64.
מגניב
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] 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) 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 של המודול שנבחר בחלונית Project. Gradle יוצר את קובצי ה-APK לכל ABI בספרייה build/outputs/apk/ של הפרויקט.
Gradle יוצר קובץ APK לכל ABI שאתם מגדירים לו כמה קובצי APK.
לדוגמה, קטע הקוד הבא של build.gradle מאפשר ליצור כמה קובצי APK לממשקי ABI של x86 ו-x86_64:
מגניב
... splits { abi { enable true reset() include "x86", "x86_64" } }
Kotlin
... splits { abi { isEnable = true reset() include("x86", "x86_64") } }
הפלט מההגדרה לדוגמה כולל את 4 קובצי ה-APK הבאים:
-
app-X86-release.apk: מכיל קוד ומשאבים ל-ABI שלx86. -
app-X86_64-release.apk: מכיל קוד ומשאבים ל-ABI שלx86_64.
כשיוצרים כמה חבילות APK על סמך ABI, Gradle יוצר רק חבילת APK שכוללת קוד ומשאבים לכל ה-ABI אם מציינים את universalApk true בבלוק splits.abi בקובץ build.gradle (ל-Groovy) או את isUniversalApk = true בבלוק splits.abi בקובץ build.gradle.kts (לסקריפט Kotlin).
פורמט שם הקובץ של ה-APK
כשיוצרים כמה קובצי APK, Gradle יוצר שמות לקובצי ה-APK לפי התבנית הבאה:
modulename-ABI-buildvariant.apk
הרכיבים של הסכימה הם:
modulename- מציין את שם המודול שנוצר.
ABI- אם מופעלים כמה קובצי APK לממשק ABI, מציין את ממשק ה-ABI של ה-APK, למשל
x86. buildvariant- מציין את וריאנט ה-build שנוצר, למשל
debug.