App Bundle 与 APK 到底差在哪?
咱们做 Android 开发的,以前发布应用就是打个 APK,扔到商店就完了。但自从 Google 推出 App Bundle(AAB)格式后,整个交付逻辑变了。我第一次接触这个是在一个体量很大的电商项目里,那时候 APK 已经超过 100MB,每次更新用户都要等好久,流失率很高。后来切换到 AAB,情况明显好转。
今天我就跟你聊聊,AAB 和传统 APK 到底有什么区别,以及为什么我建议你认真考虑迁移。
从文件结构说起
APK 是个打包好的“全量安装包”。你写一个应用,里面可能包含多国语言、多种屏幕密度资源、不同 CPU 架构的 so 库。普通 APK 会把所有资源一股脑塞进一个文件里,不管用户实际是否需要。
而 App Bundle 呢?它本质上是一个“发布包”,不是安装包。AAB 文件里存放的是你所有代码和资源的原始素材,以及模块描述文件。当用户从商店下载时,Google Play 会根据用户的设备配置(屏幕密度、语言、CPU 架构等)动态生成一个优化后的 APK——我们叫它“分片 APK”或“按需 APK”。
核心区别一句话:APK 是“一把抓”,AAB 是“按需配”。
构建与分发流程对比
先看一张对比图,我习惯用这种形式帮团队快速理解。
五个关键维度的详细区别
下面这个表格是我在团队内部培训时常用的,可以帮你快速对比。
| 对比维度 | 传统 APK | App Bundle(AAB) |
|---|---|---|
| 交付格式 | 直接是安装包(.apk) | 发布包(.aab),商店处理后再分发 |
| 资源包含 | 所有语言、密度、架构资源 | 仅打包原始资源,按需生成分片 |
| 安装包大小 | 固定,通常很大 | 按设备配置优化,平均减少 30%~50% |
| 动态模块支持 | 不支持(需要额外方案如 split APK) | 原生支持 on-demand 和 conditional delivery |
| 更新策略 | 必须下载整个新 APK | 只下载变更的分片,增量更新 |
注意:App Bundle 要求开发者通过 Google Play 发布。如果你使用第三方市场(比如国内某些渠道),可能无法完全利用按需分发能力。我在一个项目里吃过这个亏——当时为了全渠道统一,不得不走回 APK 方式。
动态交付的核心优势
明白了区别,再说说为什么我强烈建议切换。说白了,就是三个字:省流量、快安装、高灵活性。
- 减小首次安装体积:用户下载的 APK 只包含屏幕密度、语言、CPU 架构中最匹配的那一份。举个例子,中文字库只需简体中文,不用带繁体;x86_64 设备不用带 armeabi-v7a 的 so。我之前的电商项目,APK 从 110MB 降到了 56MB,安装转化率提升了 15%。
- 按需加载模块:你想想看,有些功能可能只有 10% 的用户会用到(比如特定支付渠道、高级滤镜),过去你只能全部打包进去。现在可以做成动态模块,只有在用户第一次触发时才下载。我习惯把第三方 SDK、实验性功能都放在 on-demand 模块里。
- 条件交付:可以按国家、版本号、设备特征做条件分发。比如针对中国区推送微信支付模块,针对欧洲区推送 PayPal 模块,代码逻辑写在一起但资源分开。
- 增量更新更高效:Google Play 的“增量代码”机制让你只需要下载改动的分片。对于频繁发版的应用,用户每次更新的下载量可能只有几百 KB。
我的建议:如果应用大于 60MB,或者包含多语言/多架构资源,务必考虑 AAB。即使是小应用,养成习惯用 AAB 发布也没什么坏处——毕竟 Play Console 已经默认推荐 AAB 上传了。
一个真实的“坑”
我曾经遇到过一个问题:某个动态模块使用了本地 so 库,但忘了配置 dist:module 的安装条件,结果用户手机是 32 位 arm 架构,但模块只打包了 64 位 so,导致崩溃。嗯,这里要注意——动态模块的交付条件必须和设备配置严格匹配,否则会出现运行时错误。
解决方案是在 build.gradle 中明确指定 split 策略,并在模块的 AndroidManifest.xml 中声明 dist:module 和 dist:delivery 属性。
// 模块的 build.gradle
android {
dynamicFeatures = [":feature_checkout"]
...
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}
<!-- feature_checkout 模块的清单文件 -->
<dist:module
dist:onDemand="true"
dist:title="@string/module_checkout_title">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
...
</dist:module>
这个坑让我花了半天时间排查,后来在团队里定了个规范:所有动态模块必须经过“设备配置清单”的交叉验证。你提前注意这一点,就能省掉不少麻烦。