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的整个体系,我画了一张图:

Android App Bundle 知识体系 AAB 核心 AAB vs APK 按需定制 vs 一刀切 体积减少 30%-60% 动态功能模块 功能拆分,按需加载 独立 build.gradle 配置 按需分发 install-time / on-demand conditional 条件触发 Play Feature Delivery 自动拆分APK | 管理模块下载 | 条件分发 依赖Google Play服务

从这张图可以看出,AAB不是单一的技术,而是一整套从打包、分发到安装的解决方案。它让App变得更轻、更智能,也让我们开发者有了更多精细控制的空间。

15.6 避坑指南

最后,分享几个我踩过的坑:

  • 动态模块的依赖问题:动态模块不能依赖其他动态模块,只能依赖base模块。如果你有模块间通信的需求,得用接口或者依赖注入框架。
  • 资源冲突:动态模块和base模块中如果有同名资源,编译时会报错。我建议给每个模块的资源文件加前缀,比如ar_tryon_
  • 测试困难:动态模块在本地测试时,需要配合bundletool生成APK。不能像普通模块那样直接run。我曾经因为没注意这个,浪费了半天时间。

嗯,AAB的内容就到这里。它不是一个银弹,但在Google Play生态下,确实是最优解。如果你还在用APK,不妨试试迁移到AAB,你会看到不一样的风景。


公众号:蓝海资料掘金营,微信deep3321