4. AAB文件结构详解
咱们做 Android 开发这么多年,APK 的结构闭着眼都能画出来。但 AAB 呢?我第一次解压一个 .aab 文件时,说实话有点懵——怎么多了这么多奇怪目录?今天我就带你把它彻底拆开,看看每个模块是干嘛的。
一句话总结:AAB 本质上还是一个 ZIP 包,但它的内部是按「模块」组织的。每个模块可以单独打包、单独签名、按需分发。
4.1 顶层结构一览
先解压一个 AAB 文件,看看顶层都有什么。我用 unzip -l myapp.aab 看过无数次了,最常见的目录和文件如下:
| 目录 / 文件 | 是否必选 | 作用 |
|---|---|---|
base/ | ✅ 必须 | 应用的基础模块(核心代码 + 通用资源) |
feature_xxx/ | 可选 | 按需动态交付的功能模块(如“高级滤镜”、“海外支付”) |
asset_pack_xxx/ | 可选 | 大型资源包(如高清纹理、视频缓存) |
BundleConfig.pb | ✅ 必须 | 整个 Bundle 的配置文件(protobuf 格式) |
resources.pb | ✅ 必须 | 资源索引表(类似 APK 的 resources.arsc) |
manifest/ | ✅ 必须 | 每个模块的 AndroidManifest.xml(base 和 feature 都有) |
dex/ | ✅ 必须(base) | DEX 字节码文件,每个模块有自己的 dex 目录 |
res/ | ✅ 必须(base) | 资源文件,与 APK 结构类似 |
我的习惯:拿到一个陌生 AAB,我第一步就是看它有没有 feature_* 目录。如果只有 base,那它本质上就是一个「改个壳的 APK」。真正的模块化大都会用到 feature。
4.2 base 模块——应用的核心骨架
base 是 AAB 的心脏,它必须存在,而且安装时一定会被下载到用户设备上。你想想看,没有 base 连 Application 都跑不起来。
base 模块的内部结构其实和传统 APK 非常像:
dex/—— 你的主 DEX 和所有多 DEXres/—— 布局、图片、字符串等lib/—— native so 库(按架构分文件夹)assets/—— 原始资产文件manifest/AndroidManifest.xml—— 基础应用的清单文件,包含application节点、权限声明等
避坑提醒:我曾经在混淆配置里把某个 Activity 移到了 feature 模块,但忘记了它在 base 的 AndroidManifest.xml 中有声明。结果一安装就崩溃:ClassNotFoundException。嗯,base 的清单只应该声明它自己包含的组件。
4.3 feature 模块——动态交付的“积木”
feature 模块是 AAB 最让我眼前一亮的设计。你可以把支付页面、AR 相机、直播推流这些不常用功能单独拆成一个 feature。用户只有用到时才下载,体验上几乎无感。
每个 feature 模块的结构和 base 很像:
manifest/AndroidManifest.xml—— 必须包含dist:module标签,标识dist:onDemand="true"dex/—— 该模块的代码(可含自己的 DEX)res/—— 该模块独有资源- 可以引用 base 模块的资源(通过 R 调用)
要注意的是:feature 模块有自己的 AndroidManifest.xml,里面声明的 Activity、Service 等组件是独立的。Google Play 会在下载该模块后,动态合并这些清单到运行时。
我曾经踩过的坑:一个 feature 模块引用了 base 中的 @drawable/ic_share,但我在 feature 的 build.gradle 里没有配置 dist:module 的 dist:type="feature" 和 dist:delivery="on-demand"。结果资源引用在运行时解析失败——因为 AAPT2 在打包时把 feature 的资源 ID 单独分配了,导致和 base 的不一致。解决方案是确保 feature 的 build.gradle 使用了 dynamicFeatures 依赖 base。
4.4 asset_pack 模块——大资源包
asset_pack 是为 Play Asset Delivery(PAD)准备的。假如你的游戏有 2GB 的高清纹理,放在 base 里那安装包就爆炸了。把它打进 asset_pack,可以让用户按需下载,甚至可以在游戏中途静默下载。
asset_pack 没有 dex 和 manifest,结构非常简单:
assets/—— 存放所有资源文件(纹理、视频缓存、数据库等)- 安装模式由
BundleConfig.pb或asset_pack.pb控制(install-time、fast-follow、on-demand)
小提示:asset_pack 的命名有讲究。我习惯叫 asset_pack_hd_textures,但不要太长,避免文件名超限(尤其是 Windows 上打包时)。
4.5 BundleConfig.pb——AAB 的“说明书”
这个文件是个 protobuf 序列化的二进制文件,你不能直接打开看。不过 Android Studio 的 Build > Analyze APK 或者 bundletool 可以解析它。
我用 bundletool dump config --bundle myapp.aab 看过太多次了。里面主要包含:
BundleConfig {
version: 1
compression_config {
gzip_compression: true
}
apex_config {
apex_embedded: false
}
optimization_config {
splits_config {
split_dimensions: LANGUAGE
split_dimensions: DENSITY
split_dimensions: ABI
}
}
asset_pack_config {
asset_pack: "hd_textures"
delivery_type: ON_DEMAND
}
}
- compression_config —— 控制哪些资源需要压缩(默认用 Gzip)
- split_dimensions —— 决定 APK 拆分维度(语言、密度、ABI)
- asset_pack_config —— 每个 asset_pack 的分发模式
- apex_config —— 是不是 APEX 模块(Android 10+ 系统更新用)
我个人的经验:如果发现生成的 APKS 比预期大很多,先查查 BundleConfig.pb 里的 compression_config。有一次我把 no_compression 设为了 true,结果打包后资源不压缩,包体暴涨。后来老老实实恢复默认。
4.6 其他关键文件
除了上面这些,还有几个文件值得注意:
resources.pb—— 全量资源索引表,类似 APK 的resources.arsc,只不过也用了 protobuf。它记录了所有模块的资源 ID 映射。native.pb—— 如果包含原生 so 库,这里会记录每个 so 的安装路径和架构信息。dex/DexHeader.pb—— 每个模块的 DEX 文件元数据。
你可以把这些 pb 文件理解成「清单的清单」。Google Play 在生成 APK 分片时,会依赖这些文件决定哪些 DEX 进 base、哪些进 feature、哪些资源要放在不同配置的 APK 中。
知识结构总览
下面这张图可以帮你快速回顾 AAB 的内部模块关系。我用不同颜色区分各模块类型,箭头表示「安装时依赖」。
小结
AAB 的文件结构,说白了就是让「应用的零件」可以分开存放、按需取用。base 是发动机,feature 是选装件,asset_pack 是备胎。而 BundleConfig.pb 就是整车的使用说明书。搞懂这些,后面拆分 APK 和调试动态交付时才能心里有数。