一、背景和目的
从 2021 年下半年开始,Google 要求新应用需要使用 Android App Bundle 才能在 Google Play 中发布。使用aab格式进行发布是一个不得不面对的问题。
那什么是 Android App Bundle呢?
Android App Bundle 是一种发布格式,其中包含应用的所有经过编译的代码和资源,它会将 APK 生成及签名交由 Google Play 来完成。
AAB 并不是一个插件化框架,它利用的是 Android Framework 提供的 split apks 技术来完成的,而所有安装 split apk 工作均是通过 IPC 交由 google play 完成,而不是国内插件化技术的反射代理 hook
总结起来就是:
1)Google官方插件化技术
2)通过aab格式进行动态分发
3)借助 Split Apk 完成动态加载
二、APP Bundle 与 APK 有什么不同
.aab文件 和 apk一样也是一个zip的文件, 不一样的是apk可以直接安装到手机,而.aab文件不能直接安装,需要通过Google Play 或者 bundletool 工具 生成优化后的apk才能安装到手机
Google Play 会根据.aab文件生成优化的 APK 并将其提供给设备进行安装,这也就是为什么Google需要开发者上传签名信息的原因。
三、APP Bundle包的结构
base/:应用程序的基本模块始终包含在base应用程序包的目录中
res/,lib/和assets/:这些目录与典型APK中的目录相同。
dex/:与APK不同,app bundle 将每个模块的DEX文件存储在这个单独的目录中。
root/:应用程序包的目录可能包含应用程序加载的基于Java的资源 Class.getResource(),这些文件稍后会重新定位到应用基础APK的根目录以及Google Play生成的每个多APK。
resources.pb:描述每个模块中的代码和资源,这在针对不同设备配置优化APK时非常有用。
BundleConfig.pb:提供有关包本身的信息,例如用于构建应用程序包的构建工具的版本。
四、如何使用APP Bundle
1.所需要的工具
1).Android Studio,确保Android Studio版本大于3.2,且gradle版本不小于3.2.1
2).bundletool,用来格式转换,需要到github上下载
bundletool工具参考文档:(https://developer.android.com/studio/command-line/bundletool?hl=zh-cn)
2.在app中的build.gradle文件中的android闭包中添加如下代码
android {
......
bundle {
density {
enableSplit true
}
abi {
enableSplit true
}
language {
enableSplit true
}
}
}
可以看出splits就是对各个moudle的在资源维度,ABI维度和Language维度的拆分。可以通过配置,来配置是否启用
构建多APK官方参考文档
3.生成aab格式
通过Build->Generate Signed Bundle/Apk->Android App Bundle,然后设置签名,以及要build的版本,等待一会就可以生成一个xxx.aab文件,它是一个压缩包,但还不能直接安装,要转为apk形式
4.用bundletool转为apks压缩包:
java -jar bundletool-all-1.8.0.jar build-apks --bundle=demo.aab --output=demo.apks
如何根据aab文件生成一个全量apk?
bundletool 只生成一个包含应用的所有代码和资源的 APK,以使该 APK 与应用支持的所有设备配置兼容,使用 universal 参数。
java -jar bundletool-all-0.10.3.jar build-apks --bundle=app.aab --output=all.apks --mode=universal
5.可以通过install-apks命令来安装apks
java -jar bundletool-all-0.10.3.jar install-apks --apks=my_app.apks
6.bundletool注意事项
feature模块被整合到base中
<dist:removable value="true | false" />
Android Gradle 插件 4.2开始支持此属性
bundletool v1.0 开始,如果是安装时分发的应用,bundletool 会在根据"dist:removable "决定是否将安装模块整合到base模块中。
出现 base_master_2.apk
构建 Android App Bundle 时,由以 Android 6.0(API 级别 23)或更高版本为目标平台的 App Bundle 生成的 APK 现在默认包含原生库的未压缩版本。这项优化无需设备创建库的副本,因此减少了应用占用的存储空间。如果您想要停用此优化,请在 gradle.properties 文件中添加以下代码。只有当您想要针对某个功能模块停用整合功能时,才需要在清单中设置此值。
android.bundle.enableUncompressedNativeLibs = false
如果显示错误,可能需要你打开实验特性开关
android.experimental.enableNewResourceShrinker = true
如果动态化模块上传没有没检测可能是动态化模块中manifest文件中没有添加application模块
五、APP Bundle 应用模块化
Android App Bundle 支持模块化,通过Dynamic Delivery with split APKs,将一个apk拆分成多个apk,按需加载(包括加载C/C++ libraries)
1.Bundle的 应用模块化有什么优点和缺点
优点:
1)并行开发:将应用拆分不同的模块,各个团队开发自己负责的模块。
2)加快编译时间:Gradle 的并行项目执行优化,编译系统就能够并行地编译多个模块,从而显著减少编译时间。
3)动态模块按需加载减少apk大小。
缺点:
目前市场上,App Bundles方案依托与Google Play应该才能做到业务模块的按需加载,其他的应用商店还没出现类似的功能。
2.Apks中的Apk分类:
1.Base Apk:
首次安装的apk,公共代码和资源,所以其他的模块都基于Base Apk;
2.Configuration APKs:
native libraries 和适配当前手机屏幕分辨率的资源;
3.Dynamic feature APKs:
不需要在首次安装就加载的模块。
3.如何创建动态模块
可参考官方文档[https://developer.android.google.cn/guide/app-bundle/at-install-delivery?hl=zh-cn]
- 从菜单栏中依次选择 File > New > New Module。
- 在 Create New Module 对话框中,选择 Dynamic Feature Module,然后点击 Next。
- 在 Configure your new module 部分中,完成以下操作:
- 从下拉菜单中选择应用项目的 Base application module。
- 指定 Module name。IDE 会使用此名称在 Gradle 设置文件中将该模块标识为 Gradle 子项目。当您构建 app bundle 时,Gradle 会使用子项目名称的最后一个元素在功能模块的清单中注入
<manifest split>
属性。 - 指定该模块的 package name。默认情况下,Android Studio 会建议一个软件包名称,该名称由基本模块的根目录软件包名称和您在上一步中指定的模块名称组合而成。
- 选择您希望该模块支持的 Minimum API level。此值应与基本模块的值一致。
- 点击 Next。
- 在 Module Download Options 部分中,配置选项。
- 点击 Finish。
4.Dynamic Feature moudle相关配置
gradle相关:
android{
dynamicFeatures = [':dynamicfeature']
}
//dynamicfeature/build.gradle
//plugins 在这里等同与apply plugin: 'com.android.dynamic-feature'
plugins {
// 申明说我时一个Dynamic Feature Module
id 'com.android.dynamic-feature'
}
android{
defaultConfig {
// 这里是你的模块应用id,跟清单文件中的package对应,dynamicfeature为模块名
applicationId "com.example.dynamicfeature"
}
}
dependencies {
// 自动新增此依赖,因为所有的Dynamic Feature Module都是基于base module的
implementation project(":app")
}
Manifest文件: 官方文档[https://developer.android.com/guide/app-bundle/dynamic-delivery?hl=zh-cn]
需要Manifest 声明是安装时还是使用时进行部署
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.dynamicfeature"> <!-- 我们的applicationId -->
<!-- dist:instant 是否免安装 -->
<!-- dist:title 模块名标识 -->
<dist:module
dist:instant="false"
dist:title="@string/title_dynamicfeature">
<!-- dist:delivery 层级用于包裹 -->
<dist:delivery>
<!-- dist:on-demand 指定模块按需下载 -->
<dist:on-demand />
<dist:install-time>
<!-- dist:conditions 用于包裹条件 -->
<dist:conditions>
<!-- 指定中国和香港地区不能下载该模块 -->
<dist:user-countries dist:exclude="true">
<dist:country dist:code="CN"/>
<dist:country dist:code="HK"/>
</dist:user-countries>
<!-- 指定华为手机才支持该模块 -->
<dist:device-feature dist:name="android.hardware.camera.ar"/>
<!-- 指定最小sdk21,最大sdk30 -->
<dist:min-sdk dist:value="21"/>
<dist:max-sdk dist:value="30"/>
</dist:conditions>
</dist:install-time>
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
</manifest>
5.如何根据App bundle 重构项目
5.1 App bundles项目依赖结构
从上图可以看出,使用AAB,项目的依赖结构发生了变化。有base和feature模块,在base中无法直接引用feature模块的类,feature模块可以直接依赖base模块。
5.2.基于动态化模块进行组件拆分原则
我们可以按照组件职责划分
Base-App:作为支撑业务组件、业务基础组件,依赖Common库和其他常用依赖,为feature-module提供需要的基本功能。
bridge:为module间提供通信接口。
Feature Module:这个是进行拆分的功能组件。
Common库:为Base-App提供相应的功能。和feature module不存在依赖关系,业务基础组件也不存在依赖关系。