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 和所有多 DEX
  • res/ —— 布局、图片、字符串等
  • 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 文件结构总览 .aab 包(ZIP) base/ feature_xxx/ asset_pack_xxx/ dex/ res/ lib/ assets/ dex/ res/ manifest/ assets/ BundleConfig.pb resources.pb | native.pb | DexHeader.pb | manifest/(各模块) 说明: • base 模块安装时必选;feature 按需下载;asset_pack 可按 install-time / fast-follow / on-demand 分发。 • BundleConfig.pb 控制压缩、拆分维度与 asset_pack 传递策略。

小结

AAB 的文件结构,说白了就是让「应用的零件」可以分开存放、按需取用。base 是发动机,feature 是选装件,asset_pack 是备胎。而 BundleConfig.pb 就是整车的使用说明书。搞懂这些,后面拆分 APK 和调试动态交付时才能心里有数。