1. AAB与APK的核心区别
Android App Bundle (AAB) 是 Google 于 2018 年推出的官方发布格式,取代了传统的 APK 成为 Play Console 的默认上传格式。虽然两者最终都在设备上安装为 APK,但它们在构建、签名、分发和安装阶段存在根本性差异。理解这些差异是掌握动态交付技术的基础。
| 对比维度 | APK | AAB (App Bundle) |
|---|---|---|
| 打包格式 | 单个 ZIP 压缩包,包含所有资源、代码和原生库;所有 DEX 合并为一个或多个 classes.dex | ZIP 容器,内部按模块和配置拆分为独立条目(如 base/、feature_xxx/、BUNDLE-METADATA/);resources.pb 代替 resources.arsc |
| 签名机制 | 使用 jarsigner 或 apksigner 对整个 APK 文件签名,v1/v2/v3 均可 | 使用 apksigner 对每个独立条目签名(AAB 本身不签名),最后通过 bundletool 生成签名后的 APK 时再统一签名 |
| 安装过程 | 直接安装单个 APK 到设备,所有内容一次性安装 | Google Play 或 bundletool 根据设备配置(屏幕密度、ABI、语言等)从 AAB 提取并合成一个或多个优化的 APK(称为 APK Set),再安装 |
| 多APK生成策略 | 手动维护多个 APK(如 xhdpi + arm64、xxhdpi + x86 等),需人工匹配设备配置 | 自动按维度(密度、ABI、语言、API 等级、特性模块)拆分为多个 APK,bundletool 按需合成,开发者无需手动管理多个版本 |
关键洞察:AAB 并非一个可安装格式,而是一个包含所有可能资源的“母版”。Google Play 商店在分发时动态生成最适合用户手机的 APK,使得应用体积平均缩小 15%–30%。
动手实践:在 Android Studio 中创建一个新项目,使用
Build → Build Bundle(s) / APK(s) → Build Bundle(s) 生成 .aab 文件。然后通过命令行 java -jar bundletool.jar build-apks --bundle=app.aab --output=app.apks --ks=my.keystore 验证 APK Set 的生成过程。
1.1 打包格式的架构差异
- APK:所有资源平铺在 ZIP 根目录,
resources.arsc包含所有字符串、颜色、尺寸等资源 ID 映射;DEX 文件受 64K 方法数限制,需要 MultiDex。 - AAB:使用
resources.pb(Protocol Buffers)代替resources.arsc,支持更高效的增量编译和资源压缩;模块目录结构清晰,允许多个 DEX 文件按模块拆分。 - 动态交付关键:AAB 的
base/目录包含基础模块,feature_xxx/目录包含按需分发模块,每个模块都可以独立配置安装条件。
构建配置示例(build.gradle):启用 Android App Bundle 并声明一个动态特性模块:
// 应用级 build.gradle
android {
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
// 特性模块 build.gradle (dynamic feature)
android {
defaultConfig {
moduleId = "feature_camera"
}
}
1.2 签名机制的玩法区别
- APK 签名:对整个文件内容计算摘要,签名后存储在 APK 的
META-INF/目录中;v1 使用 JAR 签名,v2/v3 使用 APK Signature Scheme,覆盖整个 ZIP 内容。 - AAB 签名:上传到 Play Console 的 AAB 本身不包含签名,仅包含签名元数据(
BUNDLE-METADATA/com.android.signing/)。Google Play 合成 APK 时使用开发者上传的密钥信息进行重新签名。 - 本地测试:使用
bundletool时需要显式指定 keystore,否则生成的 APK 无法安装(INSTALL_PARSE_FAILED_NO_CERTIFICATES)。
1.3 安装过程:从 AAB 到设备
- 上传:开发者将 .aab 文件上传至 Google Play Console。
- 生成 APK Set:Play 商店(或 bundletool)读取 AAB 的
BundleConfig.pb,根据设备提交的屏幕密度、ABI、语言、API 等级等参数,从 AAB 中提取匹配的资源,合成一个.apks压缩包。 - 签名:对每个生成的 split APK 使用 App Signing by Google Play 或开发者自己的密钥进行签名。
- 安装:设备接收到一组 split APK(base + 配置 split + 按需 feature),
PackageManager将其视为同一应用,合并安装到/data/app/下。
1.4 多 APK 生成策略对比
| 维度 | 传统 APK 策略 | AAB 策略 |
|---|---|---|
| 屏幕密度 | 手动维护 mdpi/hdpi/xhdpi/xxhdpi 等多版本 | 自动生成密度分片,设备仅下载对应密度的资源 |
| ABI | 分别打包 arm64-v8a / armeabi-v7a / x86_64 | AAB 包含所有原生库,bundletool 按需提取 |
| 语言 | 全量包含,APK 体积大 | 可配置按语言拆分,设备只下所需的语言资源 |
| 动态特性 | 不原生支持,需自建插件框架(如 Loader) | 通过 <dist:module> 在 AndroidManifest 中声明安装条件(install-time / on-demand / conditional) |
常见陷阱:如果 build.gradle 中未正确启用
bundle { ... enableSplit = true },可能导致 Google Play 仍然合成全量 APK,失去动态交付的体积优势。另外,bundletool 生成的 APK Set 默认不会包含签名的 APK,必须配合 --ks 参数或使用 --mode=universal 生成通用 APK 用于测试。
1.5 模块分割规则与资源代码拆分
AAB 的模块分割遵循以下规则:
- 资源拆分:按 density、language、mcc、country、screen 等限定符分割,每个 split APK 只包含该设备需要的资源文件。
- 代码拆分:DEX 文件按模块拆分,base 模块包含应用入口和核心代码,dynamic feature 模块的代码只在按需下载后才可用。
- 原生库:按 ABI 拆分,设备只会获取匹配的
lib/目录。 - 安装时 vs 按需 vs 条件分发:
install-time:随 base APK 一同下载安装(合并为初始 APK);
on-demand:用户运行时按需下载;
conditional:根据设备特性(如国家、API 级别、硬件功能)决定是否自动安装。