虽然最好尽可能构建单个 APK 来支持所有目标设备,但由于文件支持多个应用二进制接口 (ABI),这可能会导致 APK 非常大。缩减 APK 大小的一种方法是创建多个 APK,其中包含适用于特定 ABI 的文件。
Gradle 可以创建单独的 APK,其中仅包含每个 ABI 专用的代码和资源。本页介绍了如何配置 build 以生成多个 APK。如果您需要创建不基于 ABI 的不同应用版本,请改用 build 变体。
针对多个 APK 配置 build
如需为多个 APK 配置 build,请将 splits 代码块添加到模块级 build.gradle 文件中。在 splits 代码块中,提供一个 abi 代码块,用于指定您希望 Gradle 如何生成每个 ABI 的 APK。
针对 ABI 配置多个 APK
如需针对不同的 ABI 创建单独的 APK,请在 splits 代码块内添加一个 abi 代码块。在 abi 代码块中,提供您选择的 ABI 的列表。
以下 Gradle DSL 选项用于按 ABI 配置多个 APK:
enable(对于 Groovy)或isEnable(对于 Kotlin 脚本)- 如果您将此元素设置为
true,Gradle 会根据您定义的 ABI 生成多个 APK。默认值为false。 exclude- 以逗号分隔列表的形式指定您不希望 Gradle 为其生成单独 APK 的 ABI。如果您想为大多数 ABI 生成 APK,但需要排除应用不支持的少数 ABI,请使用
exclude。 reset()清除默认 ABI 列表。只与
include元素结合使用,以指定您想要添加的 ABI。以下代码段通过调用
reset()清除列表,然后使用include,将 ABI 列表设置为仅包含x86和x86_64: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以逗号分隔列表的形式指定您希望 Gradle 为其生成 APK 的 ABI。只与
reset()结合使用,以指定确切的 ABI 列表。universalApk(对于 Groovy)或isUniversalApk(对于 Kotlin 脚本)如果为
true,Gradle 除了会生成按 ABI 生成的 APK 之外,还会生成通用 APK。通用 APK 在单个 APK 中包含适用于所有 ABI 的代码和资源。默认值为false。
以下示例会为每个 ABI(x86 和 x86_64)生成一个单独的 APK。
为此,首先使用 reset() 创建一个空的 ABI 列表,然后使用 include 创建一个 ABI 列表,其中每个 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 所示。
模块的 Active Build Variant 值决定了将会部署并显示在编辑器中的 build 变体。如需在 build 变体之间切换,请点击模块的 Active Build Variant 单元格,然后从列表字段中选择所需的变体。
含有原生/C++ 代码的项目
对于含有原生/C++ 代码的项目,Build Variants 面板包含三列:Module、Active build 变体 和 Active ABI,如图 2 所示。
模块的 Active Build Variant 值决定了将会部署并显示在编辑器中的 build 变体。对于原生模块,Active ABI 值决定了编辑器使用的 ABI,但不会影响部署的内容。
如需更改 build 类型或 ABI,请执行以下操作:
- 点击 Active build 变体 或 Active ABI 列的单元格。
- 从列表字段中选择所需的变体或 ABI。系统会自动运行新的同步。
更改应用或库模块的任一列都会将更改应用于所有相关行。
配置版本控制
默认情况下,在 Gradle 生成多个 APK 时,每个 APK 都有相同的版本信息,该信息在模块级 build.gradle 或 build.gradle.kts 文件中指定。由于 Google Play 商店不允许同一个应用的多个 APK 全都具有相同的版本信息,因此在上传到 Play 商店之前,您需要确保每个 APK 都有唯一的 versionCode。
您可以配置模块级 build.gradle 文件,以替换每个 APK 的 versionCode。通过创建映射,为配置了多个 APK 的每个 ABI 分配一个唯一的数值,您可以使用一个值来替换输出版本代码,该值将 defaultConfig 或 productFlavors 代码块中定义的版本代码与分配给 ABI 的数值组合在一起。
在以下示例中,x86 ABI 的 APK 获得 2004 的 versionCode,x86_64 ABI 获得 3004 的 versionCode。
如果以较大的增量(如 1000)分配版本代码,那么当您以后需要更新应用时,就可以分配唯一的版本代码。例如,如果 defaultConfig.versionCode 在后续更新中迭代到 5,那么 Gradle 为 x86 APK 分配的 versionCode 将为 2005,为 x86_64 APK 分配的版本代码将为 3005。
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] 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,为 Project 窗格中当前选定的模块构建所有 APK。Gradle 会在项目的 build/outputs/apk/ 目录中为每个 ABI 创建 APK。
Gradle 会为您配置了多个 APK 的每个 ABI 构建一个 APK。
例如,以下 build.gradle 代码段支持为 x86 和 x86_64 ABI 构建多个 APK:
Groovy
... 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:包含x86ABI 的代码和资源。app-X86_64-release.apk:包含x86_64ABI 的代码和资源。
在基于 ABI 构建多个 APK 时,只有当您在 build.gradle 文件的 splits.abi 代码块中指定 universalApk true(对于 Groovy)或在 build.gradle.kts 文件的 splits.abi 代码块中指定 isUniversalApk = true(对于 Kotlin 脚本)时,Gradle 才会生成包含所有 ABI 的代码和资源的 APK。
APK 文件名格式
构建多个 APK 时,Gradle 生成的 APK 文件名采用以下方案:
modulename-ABI-buildvariant.apk
该方案由下面几部分组成:
modulename- 指定正在构建的模块名称。
ABI- 如果启用了针对 ABI 的多 APK 构建,应指定 APK 对应的 ABI,如
x86。 buildvariant- 指定正在构建的 build 变体,如
debug。