15、Android App Bundle(AAB):告别APK的笨重时代
说实话,我第一次接触AAB的时候,心里是有点抵触的。毕竟APK用了那么多年,突然要换格式,总觉得麻烦。但当我真正理解了它的设计理念,尤其是在一个大型项目中实践过后,我只能说——真香。
这一章,我们就来聊聊AAB到底是什么,它和APK有什么本质区别,以及它带来的动态功能模块和按需分发这些新玩法。
15.1 AAB与APK:到底差在哪?
先问一个问题:你每次打包APK的时候,是不是把所有资源、所有ABI、所有语言都塞进去了?
嗯,这就是APK最大的问题——它是个“大而全”的包。用户下载后,可能90%的资源根本用不上。比如一个中文用户,为什么要下载英文、日文、法文的字符串?一台ARM64的手机,为什么要包含ARM32和x86的so库?
AAB(Android App Bundle) 的思路完全不同。它不直接生成APK,而是生成一个“中间产物”。Google Play拿到这个AAB后,会根据用户设备的配置(语言、屏幕密度、CPU架构等),动态生成一个最适合该设备的APK。
说白了,AAB是“按需定制”,APK是“一刀切”。
核心区别总结:
- APK:包含所有资源,用户下载后解压使用,体积大。
- AAB:只包含逻辑和资源索引,由Play商店按需生成APK,体积小。
我在项目中遇到过这样一个场景:一个海外社交App,支持30多种语言。用APK打包,安装包直接飙到120MB。换成AAB后,用户实际下载的APK只有40MB左右。用户体验提升了一大截。
15.2 动态功能模块(Dynamic Feature)
AAB带来的另一个杀手锏,就是动态功能模块。
以前我们做模块化,都是把功能拆成多个library module,最终全部打进APK。用户不管用不用,都得下载。现在不一样了,你可以把某些功能做成动态模块,用户需要时才下载。
举个例子:一个电商App,有个“AR试妆”功能。不是每个用户都想用这个功能,对吧?把它做成动态模块,用户点击“试妆”按钮时,再触发下载。这样主APK的体积就能小很多。
怎么配置呢?看代码:
// 在项目的build.gradle中
android {
dynamicFeatures = [":ar_tryon", ":live_streaming"]
}
// 在app的build.gradle中
dependencies {
implementation project(':ar_tryon')
implementation project(':live_streaming')
}
每个动态模块都有自己的build.gradle,和普通模块类似,但需要声明:
// ar_tryon模块的build.gradle
android {
defaultConfig {
// 模块的版本号,必须和主模块一致
versionCode 1
versionName "1.0"
}
}
dependencies {
implementation project(':app')
}
小提示:动态模块的包名必须和主模块不同,否则会冲突。我刚开始就踩过这个坑,编译报错找了半天。
15.3 按需分发(On-Demand Delivery)
动态模块有了,那什么时候下载呢?这就涉及到按需分发了。
按需分发有三种模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
install-time |
安装时自动下载,和主APK一起安装 | 核心功能,但不想增大主包体积 |
on-demand |
按需下载,用户触发后才下载 | 非核心功能,如AR、游戏关卡 |
conditional |
条件触发,比如设备支持某种特性时才下载 | 硬件相关功能,如VR、高帧率模式 |
配置方式很简单,在模块的AndroidManifest.xml中声明:
<dist:module
dist:onDemand="true"
dist:title="@string/ar_tryon_title">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
这里有个关键属性dist:fusing。如果设为true,当用户设备不支持按需下载时(比如某些国产ROM),这个模块会被合并到主APK中。嗯,算是个兜底策略。
15.4 Play Feature Delivery:Google Play的智能分发
前面说的都是本地配置,但真正让AAB发挥威力的,是Google Play的Play Feature Delivery服务。
你上传AAB到Google Play Console后,Play商店会自动处理以下几件事:
- 按设备配置拆分APK:生成不同语言、不同ABI、不同屏幕密度的APK。
- 管理动态模块的下载:用户触发下载时,Play商店负责下载并安装模块。
- 支持条件分发:比如只有Android 12以上的设备才下载某个模块。
我曾经在一个项目中,把“高清纹理包”做成了conditional模式,只有GPU支持Vulkan的设备才会下载。这样低端机用户就不会被无用的资源拖累。
注意:Play Feature Delivery依赖Google Play服务。如果你的App需要在国内分发,或者用户可能没有Google Play,那就要谨慎使用。可以考虑用第三方方案,比如腾讯的Tinker或自研的插件化框架。
15.5 知识体系总览
为了让你更直观地理解AAB的整个体系,我画了一张图:
从这张图可以看出,AAB不是单一的技术,而是一整套从打包、分发到安装的解决方案。它让App变得更轻、更智能,也让我们开发者有了更多精细控制的空间。
15.6 避坑指南
最后,分享几个我踩过的坑:
- 动态模块的依赖问题:动态模块不能依赖其他动态模块,只能依赖base模块。如果你有模块间通信的需求,得用接口或者依赖注入框架。
- 资源冲突:动态模块和base模块中如果有同名资源,编译时会报错。我建议给每个模块的资源文件加前缀,比如
ar_tryon_。 - 测试困难:动态模块在本地测试时,需要配合
bundletool生成APK。不能像普通模块那样直接run。我曾经因为没注意这个,浪费了半天时间。
嗯,AAB的内容就到这里。它不是一个银弹,但在Google Play生态下,确实是最优解。如果你还在用APK,不妨试试迁移到AAB,你会看到不一样的风景。