App Bundle 入门:它到底是什么?

我第一次接触 App Bundle 是在一次线上事故之后。当时我们的 APK 已经快 80MB 了,用户抱怨下载太慢,特别是海外地区。后来我试了 Android App Bundle(AAB),发现它并不是一种新的安装包格式,而是一种 发布格式。说白了,你上传到 Google Play 的是 .aab 文件,用户真正下载到手机上的还是 APK——只不过这个 APK 是按需定制的。

Apple 那边早就用了类似思路(App Thinning),Android 这边直到 2018 年才推出。嗯,迟到了几年,但效果确实好。

AAB 的工作原理:Google Play 替你“切”包

你可以把 .aab 想象成一个原材料仓库。里面装着你所有的代码、资源、原生库、语言包等。用户点击安装时,Google Play 的服务端会根据用户的设备信息(屏幕密度、CPU 架构、语言等),只提取出需要的那部分,组装成一个“瘦身版”APK 交付给用户。

核心点:AAB 不是 APK 的替代品,而是 APK 的生成配方。

举个例子:你的应用同时支持 armeabi-v7a 和 arm64-v8a。用户 A 用的是 64 位手机,下载到的 APK 只包含 64 位的 so 文件;用户 B 用 32 位手机,则只拿到 32 位的 so。以前用传统 APK,你得把所有架构的 so 都塞进去,白白浪费存储和流量。

AAB vs APK:5 个核心区别

对比项 APK AAB
用途 安装包(直接安装) 发布格式(提交商店)
能否直接安装 可以 不可以(无签名,不完整)
文件大小 较大(包含所有资源) 较小(模块化,但包含全部可能的资源)
用户下载量 全量包 按需生成的瘦身包
动态分发 不支持 支持(动态功能模块、按需加载)

说白了,APK 是成品,AAB 是半成品。你不可能把一个 .aab 文件通过 adb 直接安装到手机上——我当年第一次拿到 .aab 时还愣了半天,因为没有安装按钮。后来才明白,需要借助 bundletool 把它转成通用的 APK。

为什么 Google 要推 AAB?

理由很简单:减少用户下载量。Google 官方数据显示,使用 AAB 后应用的下载大小平均降低 20% 以上。你要是做过国际化应用,就知道这个数字有多香。我在印尼本地化的时候,当地用户很多还是 3G 网络,甚至 2G,少 10MB 安装包就能显著提高转化率。

另外,AAB 还引入了分阶段部署(Staged Rollout)。你可以给不同的用户群体(比如内测组、5% 的稳定版用户)颁发不同的签名密钥,然后在服务器端控制更新的节奏。这一点我们在后面章节会反复用,先记住这个概念。

小提示:国内应用商店目前大多不支持 AAB,所以如果你的应用只面向国内市场,暂时还是用传统 APK。但是做海外发行,AAB 已经成为必然选择——Google Play 从 2021 年 8 月起已经强制要求新应用必须使用 AAB 发布了。

AAB 的内部结构

我拆解过一个典型的 .aab 文件,它的内部跟 APK 大不相同。来看看关键目录:

base/
├── manifest/
│   └── AndroidManifest.xml
├── dex/
│   └── classes.dex / classes2.dex ...
├── res/
│   ├── drawable-mdpi/
│   ├── drawable-hdpi/
│   └── drawable-xhdpi/ ...
├── lib/
│   ├── armeabi-v7a/
│   └── arm64-v8a/ ...
├── assets/
├── root/
└── ...

注意到了吗?资源目录按密度、语言等维度拆分得很细。在打包成 APK 时,Google Play 会基于这个结构只选取与你设备匹配的条目。比如你的手机是 320dpi(xhdpi),那么 mdpi、hdpi 的图片就不会被打进最终的 APK 里。

还有一个重要的文件是 BundleConfig.pb,它指定了哪些模块是“按需”的,哪些是“随基座安装”的。这是分阶段部署的配置入口。

我曾经踩过的坑:多语言资源不全

有一次我测试 App Bundle 的沉浸式翻译功能,发现某些语言界面显示不全。排查了半天,发现是因为我漏掉了 playcore 打包规则中的一个配置。默认情况下,用户下载只包含他手机语言对应的资源字符串。如果我希望所有用户都能看到某个特殊语言的内容,必须在模块配置里强制保留。嗯,这个坑在后面第 15 章讲动态模块时会详细展开,这里你先有个印象。

为了避免这种问题,官方提供了 bundletool 来模拟不同设备生成 APK 并验证。

# 生成指定设备的 APK
java -jar bundletool.jar build-apks \
  --bundle=my_app.aab \
  --output=my_app.apks \
  --ks=my_keystore.jks \
  --ks-pass=pass:password \
  --key-pass=pass:password

# 安装到设备
java -jar bundletool.jar install-apks \
  --apks=my_app.apks

我通常会先用 --device-spec 参数指定一个 480dpi、英文、arm64-v8a 的虚拟设备来测试,确保切片逻辑正确。

你真正需要掌握的核心知识体系

为了让你一眼看清全貌,我把这一章的知识结构画成了图:

App Bundle 知识体系 Android App Bundle (.aab) 定义:发布格式 工作原理:服务端切片 核心区别:5 点 不可直接安装 包含所有模块 按设备切片 生成瘦身APK 用途不同 下载量减少 bundletool 工具 Google Play 签名 分阶段部署入口 动态功能模块 目标:更小的下载包 + 更快的更新 分阶段部署是AAB最重要的高级特性之一 后续章节会手把手带你实现它

我的建议:刚开始不用去背 AAB 的内部结构表,你只要知道它的切片思想就够了。先动手用 Android Studio 打包一次 .aab,再用 bundletool 转成 APK 装到手机上跑跑看,比看十篇文档都管用。

注意:如果你使用 Instant App 或者 on-demand 动态模块,AAB 文件的大小上限跟 APK 不一样,部分内容不能超过 150MB。后面部署的时候我们会专门讲这个限制。