构建多个 APK (APK Splits)
APK Splits 允许为不同的设备配置构建多个优化的 APK,每个 APK 仅包含特定设备需要的资源和代码。
APK Splits vs AAB
⚠️ 重要:Google Play 现在推荐使用 Android App Bundle (AAB),它能自动处理分发优化。仅在以下情况使用 APK Splits:
- 不通过 Google Play 分发
- 需要手动控制 APK 生成
- 分发到不支持 AAB 的应用商店
APK Splits 类型
按屏幕密度拆分
为不同的屏幕密度生成单独的 APK:
kotlin
android {
splits {
density {
enable = true
// 指定要生成的密度
include("ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi")
// 排除某些密度
exclude("ldpi")
}
}
}groovy
android {
splits {
density {
enable true
include 'ldpi', 'mdpi', 'hdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi'
exclude 'ldpi'
}
}
}生成的 APK:
app-mdpi-release.apk- 包含 mdpi 资源app-hdpi-release.apk- 包含 hdpi 资源app-xhdpi-release.apk- 包含 xhdpi 资源- ...
按 ABI 拆分
为不同的 CPU 架构生成单独的 APK:
kotlin
android {
splits {
abi {
enable = true
// 重置默认列表
reset()
// 指定要生成的 ABI
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
// 是否生成通用 APK(包含所有 ABI)
isUniversalApk = true
}
}
}groovy
android {
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
}生成的 APK:
app-armeabi-v7a-release.apk- 32位 ARMapp-arm64-v8a-release.apk- 64位 ARMapp-x86-release.apk- 32位 x86app-x86_64-release.apk- 64位 x86app-universal-release.apk- 包含所有 ABI(如果启用)
同时按密度和 ABI 拆分
可以同时启用两种拆分,Gradle 会为每个组合生成 APK:
kotlin
android {
splits {
density {
enable = true
include("hdpi", "xhdpi", "xxhdpi")
}
abi {
enable = true
include("arm64-v8a", "x86_64")
}
}
}生成的 APK 组合:
app-hdpi-arm64-v8a-release.apkapp-hdpi-x86_64-release.apkapp-xhdpi-arm64-v8a-release.apkapp-xhdpi-x86_64-release.apkapp-xxhdpi-arm64-v8a-release.apkapp-xxhdpi-x86_64-release.apk
配置版本号
为每个 APK 分配唯一的 versionCode,确保 Google Play 能正确管理更新。
版本号策略
推荐的版本号格式:AABBIIII
AA:通用版本号BB:ABI 代码IIII:密度代码
kotlin
android {
defaultConfig {
versionCode = 1
versionName = "1.0"
}
splits {
abi {
enable = true
reset()
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
density {
enable = true
include("hdpi", "xhdpi", "xxhdpi")
}
}
}
// ABI 代码映射
val abiCodes = mapOf(
"armeabi-v7a" to 1,
"arm64-v8a" to 2,
"x86" to 3,
"x86_64" to 4
)
// 密度代码映射
val densityCodes = mapOf(
"hdpi" to 1,
"xhdpi" to 2,
"xxhdpi" to 3,
"xxxhdpi" to 4
)
androidComponents {
onVariants { variant ->
variant.outputs.forEach { output ->
val name = output.filters.find { it.filterType == FilterType.ABI }?.identifier
val abiCode = abiCodes[name] ?: 0
val density = output.filters.find { it.filterType == FilterType.DENSITY }?.identifier
val densityCode = densityCodes[density] ?: 0
output.versionCode.set(
(variant.versionCode.get() ?: 1) * 10000 + abiCode * 100 + densityCode
)
}
}
}groovy
android {
defaultConfig {
versionCode 1
versionName "1.0"
}
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
density {
enable true
include 'hdpi', 'xhdpi', 'xxhdpi'
}
}
}
def abiCodes = [
'armeabi-v7a': 1,
'arm64-v8a': 2,
'x86': 3,
'x86_64': 4
]
def densityCodes = [
'hdpi': 1,
'xhdpi': 2,
'xxhdpi': 3,
'xxxhdpi': 4
]
androidComponents {
onVariants { variant ->
variant.outputs.each { output ->
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
def abiCode = abiCodes[abiName] ?: 0
def densityName = output.getFilter(com.android.build.OutputFile.DENSITY)
def densityCode = densityCodes[densityName] ?: 0
output.versionCodeOverride =
variant.versionCode.get() * 10000 + abiCode * 100 + densityCode
}
}
}版本号示例:
如果基础 versionCode = 1:
app-armeabi-v7a-hdpi-release.apk→10101app-arm64-v8a-xhdpi-release.apk→10202app-x86_64-xxhdpi-release.apk→10403
构建 APK Splits
bash
# 构建所有 release APKs
./gradlew assembleRelease
# 构建所有 debug APKs
./gradlew assembleDebug输出位置:
app/build/outputs/apk/release/
├── app-arm64-v8a-release.apk
├── app-armeabi-v7a-release.apk
├── app-x86-release.apk
├── app-x86_64-release.apk
└── app-universal-release.apkAPK 文件名格式
默认格式:{module}-{density}-{abi}-{buildType}.apk
示例:
app-xxhdpi-arm64-v8a-release.apkapp-xhdpi-x86-debug.apk
测试 APK Splits
安装特定配置的 APK
bash
# 查看设备 ABI
adb shell getprop ro.product.cpu.abi
# 安装特定 APK
adb install app-arm64-v8a-release.apk安装Universal APK
Universal APK 包含所有配置,适合测试:
bash
adb install app-universal-release.apk按语言拆分
⚠️ 注意:按语言拆分主要用于 Android Instant Apps。
kotlin
android {
bundle {
language {
enableSplit = true
}
}
}最佳实践
优先使用 AAB:
- Google Play 自动处理拆分
- 无需手动管理版本号
- 自动优化
仅在必要时使用 APK Splits:
- 第三方应用商店分发
- 企业内部分发
- 特殊分发需求
合理选择拆分维度:
- ABI 拆分:减少 APK 体积最明显(原生库通常很大)
- 密度拆分:对图片资源多的应用效果显著
- 避免过度拆分:管理成本增加
保留 Universal APK:
- 作为备用(设备不支持特定配置时)
- 便于测试
- 设置
isUniversalApk = true
正确配置版本号:
- 确保每个 APK 有唯一 versionCode
- 使用合理的版本号策略
- 遵循 Google Play 要求
测试所有 APK:
- 在对应设备配置上测试
- 验证资源正确性
- 检查 APK 大小
APK Splits vs ndk.abiFilters
ndk.abiFilters:过滤包含的 ABI,但仍生成单个 APK。
kotlin
android {
defaultConfig {
ndk {
abiFilters += listOf("arm64-v8a", "x86_64")
}
}
}APK Splits:为每个 ABI 生成单独的 APK。
选择:
- 简单场景:使用
abiFilters - 需要优化体积:使用 APK Splits
- Google Play发布:使用 AAB