什么是AAB?
AAB全程Android App Bundle,是由谷歌推出的一种,面向应用开发者&应用分发渠道(商店) 的特殊应用打包格式
谷歌于2021年起,已强制要求所有app上传AAB到应用商店,不允许再上传apk。
AAB诞生前的故事
远古时代
远古时代,应用开发者上传完整的apk包至应用商店,最终分发给用户的也是完整的apk包:
这会导致用户安装在设备上的apk里,包含了许多用户不需要的功能:
- 低分辨率的手机,安装包里却有高分辨率的图片
- 英文的手机,安装包里却有100+种语言
- 32位的手机,安装包里却有64位的so文件
- 欧美的手机,安装包里却有只面向亚洲的功能代码
中世纪
为此,Google Play Store发明了............发明了multiple apks!
developer.android.com/google/play…
看下这个恐怖的压缩包:
2个abi纬度x7个dpi纬度 = 14个apk
多一个x86 abi?多一个xxxxhdpi?多一个语言纬度?100个语言?? ➡️ 爆炸的apk数量
动态切换语言?动态代码下发???不太可能做到 ➡️ 不够追求极**致
AAB的诞生
由于上述问题,Android Lolipop中悄咪咪地引入了一个新功能:split apk
- 允许一个包名下安装多个apk
- 每个apk都是完整的apk结构,必含
AndroidManifest.xml、resources.arsc、META-INF签名,可选包含classes.dex - AOSP会在app打开时自动读取所有split apk到classloader、AssetsManager中
- 除了主包外,其余包需在
AndroidManifest.xml中添加split=xxx,表明为split包
为什么 AOSP 要标记为split包,而不让split包和base包平级?
从AOSP的代码/功能中,可以猜测几个原因:
-
安卓的古早代码,就是从一个apk里解析~~~~ApplicationInfo~~~~的,多个apk的兼容逻辑不好写 -
为了区分app安装split apk还是覆盖升级的情况
-
AOSP一开始就准备为split apk做
isolatedSplits模式,这个模式下:每个split apk之间的class、resource不再合并在一起,而是各自独立,能共享访问的只有base包。
除了split apk之外,谷歌对apk分发还有更多考量:
- 谷歌还需要一种比,把所有split apk打包成zip,更高效的包格式,以节省网络带宽
- 谷歌希望自己有机会对apk包进行更多透明的包体积优化
- 排列组合、整体大包在一些情况下依然需要,谷歌不想为此造成过多的带宽浪费
于是,谷歌推出了全新的AAB文件格式:
- 开发者上传AAB文件到Google Play,Google Play再生成各个split apk分发给用户
- Play Store再根据用户手机配置,按需安装所有需要的split apk。
- AAB文件格式易于谷歌商店解析,以便于再生成apk
- AAB文件内容相比apk,更加紧实:
183MB的AAB生成202MB的完整apk
- AAB格式大量应用protobuf:pb格式的aab描述文件、pb格式的arsc/AndroidManifest.xml
- AAB作为中间产物,可以携带部分仅面向开发者的元信息,如:proguard mapping
同时,谷歌开源了将AAB编译为apk的工具:bundletool
Q:不支持split apk(<5.0)的设备,就不能使用AAB了吗?
A:也是可以使用AAB格式的。bundletool除了默认模式,提供了另外两种模式:universal和standalone。
universal模式时,AAB将不再分资源apk,会生成一个包含了所有资源的universal.apk
standalone模式时,AAB会排列组合abi、dpi来生成apk
standalone其实不是一个独立的bundletool模式,而是默认模式的附加产物。
当app的minsdk>19(>安卓4.4)时,standalone apk将不再会生成
Q:谷歌这个神奇的网站是怎么统计到数据的?play.google.com/sdks/catego…
A:每个AAB包里都记录了这个app所用到的library的信息:
灵活的AAB多语言
当一个应用AAB化后,Google Play在给用户安装此app时,只会安装系统需要的语言包
手机如果如上图设置,Google Play Store就只会为app安装简体中文、日文、繁体中文(香港/台湾)的语言包
同时,Google Play也可以为app动态添加语言:
-
动态语言下发: app可以请求安装新的语言包(如TikTok的设置-语言页),这时候app就需要在运行时向Google Play请求安装一个语言包。这就是动态语言下发。
-
自动语言包更新: Google Play Store会注册
android.intent.action.LOCALE_CHANGED广播,当系统语言发生更改时自动为app安装新的语言包
不是有很意思的小知识
Google Play收到android.intent.action.LOCALE_CHANGED广播时,app还是运行着的,Google Play会怎么办?
A. 等待app退出,再安装语言包
B. 在app运行时,偷偷为app装上语言包
C. 通知app,让其发起新语言包请求
D. 钝角
正确答案是:◼︎(刮开屏幕,查看正确答案)
Dynamic Feature简介
Dynamic Feature体系类似于国内的插件化体系(如Mira),但是解决的问题不同:
国内的插件化体系,是为了减少包体积、业务代码热更新;而Dynamic Feature只是为了减少包体积,不具备热更新能力。
开发者可以将不常用的功能制作成独立于核心包的dynamic feature包,并配置只在一些情况安装。谷歌目前允许两种Dynamic Feature安装方式:
- install-time:在app安装时,根据开发者指定的条件(例如,只在美国安装、只在Android>7安装),顺带安装
- on-demand:app在运行时,主动向Google Play Store请求动态安装
| Dynamic Feature | Mira | |
|---|---|---|
| 插件版本 | 与宿主保持一致,不允许不一致 | 允许不同插件版本 |
| 安装途径 | Play Store帮安装、app自行安装 | app自行安装 |
| 分发方式 | Google Play服务器 | 自建服务器 |
| 安装方式 | Split apk(PlayStore安装)/反射classloader注入(自行安装) | 反射classloader注入 |
| 编译工具链 | AGP原生支持 | Mira魔改AGP支持 |
以上配置规则翻译成中文就是:
-
谷歌小程序(Google Play Instant):否
-
Dynamic Feature对用户展示的标题:Oppo Push Module
-
Google Play Store帮我们安装:
- 除了国家是US时,都安装
-
Fusing至Universal APK中:是
最终Dynamic Feature编译至AAB中:
AAB用bundletool生成的结果:
Dynamic Feature 在不支持split apk(<5.0)的系统上,怎么办?
bundletool提供了universal模式,允许生成包含了所有资源的universal.apk。
那么dynamic feature默认也会进入universal apk吗?
默认是不会的!DF模块必须在AndroidManifest.xml中<dist:fusing dist:include="true" />,才会被集成到universal.apk中
Dynamic Feature下发
当Google Play判断Dynamic Feature符合直接安装条件时,就会将Dynamic Feature apk作为split apk,在app首次安装/更新时顺带安装
当Google Play未自动安装Dynamic Feature时,开发者可以手动请求Dynamic Feature安装,即on-demand模式:
developer.android.com/guide/playc…
// 创建一个SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)
// 创建安装Dynamic Feature的请求
val request =
SplitInstallRequest
.newBuilder()
// 想装几个装几个
// 这些模块会在一个session里一起装上
.addModule("pictureMessages")
.addModule("promotionalFilters")
.build()
splitInstallManager
// 需要在前台提交请求,然后请求就会异步开始
.startInstall(request)
.addOnSuccessListener { sessionId -> ... }
.addOnFailureListener { exception -> ... }
当我们发起下载后,会发生的事:
- Dynamic Feature SDK会将请求发送给Play Store,Play Store开始Dynamic Feature下载
- Play Store在DF下载过程中,会随时向app发送进度回调
- Play Store下载完成后,会将df apk的content resolver uri发送给app
- app读取Play Store下载好的df apk,验证,解包,解压到app的
data目录下
至此,Dynmiac Feature的“Install Request”就算完成了
然后就可以正常运行Dynamic Feature里的代码了!吗?
其实,完成上述步骤后,Dynamic Feature其实并没有“Install”(虽然回调叫做INSTALLED),离Install还差最后一步:
SplitCompat.install(context)
SplitCompat如其名,就是在运行时“compat” split apk的helper类。其工作原理,就是将解包好的dex、classloader、arsc反射到app中。
很蠢对吧!谷歌自己发明的AOSP、GMS,居然还是用这么黑科技的方法为app启用on-demand Dynamic Feature?开发者能忍谷歌自己也不能忍!
on-demand没用到split apk?安卓4.4在技术上也兼容运行时 DF ?
没错!Dynamic Feature的运行时安装法,在安卓4.4的设备上也是能正常运行的!但是谷歌出于一致性的考量,禁用了这个用法。
但是我们通过反编译Play Store,就能发现,谷歌给一些app特别开了后门(如facebook),他们的一些app的确可以在4.4的设备上要求Play Store下发Dynamic Feature给他们。
不是有很意思的小知识
on-demand的Dynamic Feature在app升级后会发生什么?
A. Google Play会在升级时顺便用install-time方式安装Dynamic Feature
B. Play Store会通过修改app data目录下df文件的方法升级Dynamic Feature模块
C. App在启动后会自动开始Dynamic Feature的升级
D. Dynamic Feature被卸载
正确答案是:◼︎(刮开屏幕,查看正确答案)