Skip to content

构建多个 APK (APK Splits)

源:Android 官方文档 - 构建多个 APK

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位 ARM
  • app-arm64-v8a-release.apk - 64位 ARM
  • app-x86-release.apk - 32位 x86
  • app-x86_64-release.apk - 64位 x86
  • app-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.apk
  • app-hdpi-x86_64-release.apk
  • app-xhdpi-arm64-v8a-release.apk
  • app-xhdpi-x86_64-release.apk
  • app-xxhdpi-arm64-v8a-release.apk
  • app-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.apk10101
  • app-arm64-v8a-xhdpi-release.apk10202
  • app-x86_64-xxhdpi-release.apk10403

构建 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.apk

APK 文件名格式

默认格式:{module}-{density}-{abi}-{buildType}.apk

示例

  • app-xxhdpi-arm64-v8a-release.apk
  • app-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