Android App Bundles相关概念及开发流程详解

主要参考文章如下。

https://developer.android.com/guide/app-bundle/
https://developer.android.com/platform/technology/app-bundle/
什么是Android App Bundle
Android App Bundle是一种新的Android App开发,编译和发布机制,它也可以用来表示这种机制生成文件的格式。在文章https://developer.android.com/guide/app-bundle/中并没有明确的区分这两者,它有时表示这种机制,有时表示文件格式。此外,它的写法也很随意,有时用首字母大写的形式Android App Bundle,有时用全小写的android app bundle,有时又把Android或android省掉,变成App Bundle和app bundle,bundle和bundles的单复数使用也很随意。

bundle是一捆,一束的意思,正如这个词的字面意思,Android App Bundle会将整个App编译成一个bundle,在这个bundle里包含了若干个子的模块,每个模块都可以拆分成一个单独的apk。这一点会在后面介绍。

Android App Bundle和Android 9
有人可能认为Android App Bundle和Android 9是捆绑在一起的,但其实并不是,它们两除了都是今年发布的之外并无其他关系。

顺便吐槽下Android Pie的版本号,所有官方文档中都将Android Pie的版本号称为Android 9,而不是Android 9.0,但在这之前的其他版本一直都是有.0的小尾巴的。然后在Android Studio中很多地方选择版本号时,Android Pie的版本号又变成了9.0。

Android App Bundle和Android Studio 3.2
Android Studio 3.2中增加了Android App Bundle的支持,包括在Build中增加了Build Bundles和Generate Signed Bundle的选项,在new module时增加了Dynamic Feature Module的模板等,这些在以前版本中是没有的。所以如果要方便的使用Android App Bundle,需要使用Android Studio 3.2及以上版本。

在Android Studio中使用Android App Bundle来开发
如前所述,Android App Bundle是一种新的Android App开发,编译和发布机制。在Android App Bundle出现之前,开发者开发一个App一般都是在Android Studio中创建一个Phone Module,把App的框架和主要功能的代码写到Phone Module中,创建0到多个Library Module,把一些相对独立的子功能代码写到Library Module,然后将整个工程编译成一个apk文件。apk文件中包含了工程中的全部代码和资源,无论是Phone Module还是Library Module都被编译到了apk文件中。

Android App Bundle在原先的Phone Module和Library Module基础上增加了一个Dynamic Feature Module,可以在开发时创建0到多个Dynamic Feature Module,正如这三个英文单词所表示的,每个Dynamic Feature Module表示一个动态的功能模块,关于这个模块的用途需要结合后面的split APKs和Dynamic Delivery来理解。这里只是介绍它的创建过程。

Dynamic Feature Module的创建
在Android Studio 3.2中,选择File - New - New Module…,打开New Module窗口,选择第三个Dynamic Feature Module,然后选择一个Base application module,输入Module name和Package name,选择Mininum API Level即可创建一个Dynamic Feature Module。Module name,Package name和Mininum API Level的含义和创建其他Module是一样的,这里多出来一个Base application module,它表示这个要创建的Dynamic Feature Module所依附的Base module,它必须是一个application module即在build.gradle中第一行是apply plugin: 'com.android.application’的module,不能是apply plugin: ‘com.android.library’。这一般就是工程的主module。Base module和Dynamic Feature Module的关系同样要到后文介绍split APKs和Dynamic Delivery的时候才能够理解。


创建好后的Dynamic Feature Module后,会在base module的build.gradle中增加android.dynamicFeatures配置,表示依赖此module的所有Dynamic Feature Modules。

android {

// Specifies dynamic feature modules that have a dependency on
// this base module.
dynamicFeatures = [":my_dynamic_feature1", “:my_dynamic_feature2”]
}

在创建的Dynamic Feature Module的build.gradle中,第一行不再是apply plugin: ‘com.android.application’,也不是apply plugin: ‘com.android.library’,而是apply plugin: ‘com.android.dynamic-feature’。同时,在创建的Dynamic Feature Module的build.gradle中还会自动添加对base module的依赖。

dependencies {

// Declares a dependency on the base module, ‘:app’.
implementation project(’:app’)
}

上述过程都是在Android Studio中创建Dynamic Feature Module时自动完成的,无需手动添加。不过如果要修改一个已有的Dynamic Feature Module的名字,并不会自动修改base module中dynamicFeatures配置中的名字,需要手动修改一下,这可能是现阶段Android Studio的一个bug,应该不久就会被修复。

使用Android App Bundle方式来编译
在Android Studio 3.2中,选择Build - Build Bundle(s) / APK(s) - Build Bundle(s)即可使用Android App Bundle方式来编译。如果要生成带签名的Bundle,则选择Build - Generate Signed Bundle /APK…,在弹出的对话框中选中Android App Bundle即可。

使用Android App Bundle方式编译后不再生成apk文件,而是生成一种新的aab格式的文件。

aab文件
aab文件是Android App Bundle编译后生成的文件格式。不同于以往的apk文件,aab文件并不能直接在Android设备上安装和运行,aab格式的文件是专门用来提交到Google Play使用的。Google Play使用后文所说的split APKs机制将一个aab文件转换为若干个apk文件,用户在下载app的时候,再通过Dynamic Delivery将用户需要的apk文件提供给用户。

和apk文件一样,aab文件同样是一个zip格式的压缩文件,可以使用7-zip等工具解压查看。

一个典型的aab文件结构如下,它包含了一个base文件夹和若干个其他文件夹,还有一个BundleConfig.pg文件。base文件夹对应app的application module,也就是主module。其他文件夹每个都对应一个Dynamic Feature Module,文件夹名就是之前创建Dynamic Feature Module使用的module name,这里是my_dynamic_feature1和my_dynamic_feature2。进入到base文件夹或每个Dynamic Feature Module对应的文件夹里,可以看到它们的结构都是一样的,包含dex,manifest,res和root四个文件夹,和一个resources.pb文件,root文件夹又包含kotlin和META-INF两个子文件夹。

将这个结构和apk文件的结构做对比,可以很容易发现它们之间基本是可以对应上的,dex文件夹存放的是apk结构中的classes.dex,manifest文件夹中存放了AndroidManifest.xml,res还是以前的res,root则包含了以前的kotlin和META-INF。从这里可以看出一个app bundle文件包含了工程全部的module代码和资源,但是它将base module和Dynamic Feature Modules独立开来,base module和每个Dynamic Feature Module都包含各自的代码和资源,它们组成了原先apk文件的内容。从后面的介绍可以知道,Google Play确实是将aab文件又重新将base module和Dynamic Feature Modules拆分成单独的apk文件。

集成Play Core Library
如果要使用Dynamic Feature Module,就需要在base module中集成Play Core Library。在后文中会看到,用户在Google Play下载一个通过Android App Bundle 方式开发的应用时,只会下载base module对应的apk文件,Dynamic Feature Module对应的apk文件会在运行时按需下载。

Play Core Library用来在App运行时请求下载Dynamic Feature Module对应的apk,它的使用还是比较复杂的,可以参见官方文档 https://developer.android.com/guide/app-bundle/playcore,这里就不多做介绍了。

multiple APKs
multiple APKs是Google Play支持的一项服务,它允许开发者在为同一个版本的APP提供多个APK文件,这些APK必须使用相同的签名和包名,但version_code必须不同。这些APK在CPU architectures,screen sizes and densities,device feature,platform versions,OpenGL texture compression formats五个维度上存在一处或多处差异,用户在下载APP时会根据设备信息选择最匹配的那个APK来下载。multiple APKs最早似乎叫做Apk splits,后来才称作multiple APKs,不过这点已无法查证,如果读者有知道这段历史的可以说下。关于multiple APKs可以参见这个文章https://developer.android.com/google/play/publishing/multiple-apks。

multiple APK虽然已经存在很久了,但操作和管理起来太复杂。首先它有5个不同的维度,但只有CPU architectures和screen densities支持在gradle中配置,可以用同一个工程一次性编译出不同CPU architectures和screen densities的APK(参见https://developer.android.com/studio/build/configure-apk-splits),其他维度的拆分则需要手动修改manifest,或创建多个工程来分别编译。如果要支持多个维度下的拆分,意味着工作量会呈指数级增长,apk文件的数量也会十分庞大。以下是在Gradle中启用了abi和screen densities后编译得到的apk文件,足足有35个,如果还需要按其他维度拆分,apk的数量会更多。每次打包都要将这几十,甚至上百个不同的apk文件一一上传到Google Play上(Google Play还不支持一次上传多个APK文件),再加上需要使用的不同的版本号,管理起来十分困难。而实际能产生的收益又很小,对CPU architectures,screen sizes and densities来说,只是减少了部分用户下载APP的大小,所以这项特性并没有被广泛使用。

split APKs
Google开发文档中并没有专门关于split APKs的文章,只在介绍app bundle的文章中用了一节来介绍。

在前面介绍Android App Bundle编译时我们知道,Android App Bundle会将一个Android工程编译成一个aab文件,但是aab文件并不能直接在Android设备上使用,必须将其重新组织成apk文件。split APKs就是用来对aab文件进行拆分和重新组装apk文件的一项服务。如前所述,一个aab文件中包含了一个base module和若干个dynamic delivery modules。split APKs会据此将一个aab文件拆分为一个基础APK和若干个功能APK,此外它还允许继续按照屏幕密度,abi和语言对基础APK做进一步的拆分,基础APK会拆分为一个Base APK和若干个Configuration APKs,每个功能APK会拆分为一个Dynamic feature APK和若干个对应的Configuration APKs。也就是说一个aab会被拆分为一个Base APK,若干个Dynamic feature APKs和更多的Configuration APKs。官网文档中的这张图很好说明了一个aab文件经过拆分之后的多个APK之间的联系。

split APKs运行在Google Play服务端,它以aab文件作为输入,每当开发者通过Google Play Console上传一个aab文件,split APKs就会将其拆分成各种apk文件。不过要想支持这种拆分后的APK文件的安装和使用,必须要Android系统的支持,在Android 5.0及之后版本上才支持split APKs。

split APK和上面的multiple APKs完全不是一回事。虽然multiple APKs也可以按照屏幕密度,abi等拆分apk,但multiple APKs机制下每个APK文件都是一个完整的安装包,它在设备上安装后就可以使用应用全部的功能,它的安装和运行也不需要在Android系统上做额外的支持。multiple APKs的生成完全交由开发者实现,Google Play只是在用户下载的时候加了一层判断,根据用户设备选择合适的APK来下载。此外,split APKs和multiple APKs拆分apk的维度也是有所差异的,split APKs支持按照功能来拆分APK,multiple APKs并不支持这一点,反过来multiple APKs支持按照device feature,platform versions,OpenGL texture compression formats来拆分APK,而split APKs并不支持。

Dynamic Delivery
Dynamic Delivery是Google Play新的应用服务模型(Google Play’s new app serving model),这个名词一般用首字母大写的形式。Dynamic Delivery同样运行在Google Play的服务端。

当一个用户通过Google Play商店下载一个App的时候,如果这个App是用app bundle机制生成的(也就是说App开发者上传到Google Play的是app bundle生成的文件,而不是apk文件),Dynamic Delivery就开始工作。它会根据用户的设备信息,来决定要将哪些apk文件分发给用户。此外,当用户运行到某项Dynamic Feature Module的功能,而这个moudle对应的apk又没有安装时,会通过Play Core Library去下载对应的apk文件,这时同样Dynamic Delivery需要根据用户设备信息将需要的Dynamic Feature Module对应的apk及其configration apks分发给用户。

使用App Bundle的优势
不再需要管理multiple APKs了
用户要下载的apk体积更小
按需提供功能,而不是一次性在安装时就全部提供
允许不安装就直接使用某项功能,需要结合instant run一起使用
 

Android App Bundle是一种改进的应用程序打包方式,能大幅度减少应用体积。简而言之,可以理解Google 官方的动态发布方案。

好处

Size更小

  • Google has claimed that it can reduce app sizes up to 50 percent
  • Its own apps like YouTube and other apps like LinkedIn which saw a 23% file reduction
  • 安装更快
  • 支持动态发布

限制

  • 仅限于通过 Google Play 发布的应用,(Google进一步巩固自身生态)
  • 需要加入到 Google 的 beta program enroll your app in app signing by Google Play in the Play Console
  • 最低支持版本Android 5.0 (API level 21)
  • 低于Android 5.0 (API level 21) 的版本GooglePlay会优化Size,但不支持动态交付。

成本

  • 需要升级到Android Studio 3.2修改工程以便支持App Bundle格式
  • 集成Play Core Library

原理

只须在 Android Studio 中构建一个应用束 (app bundle),就可以将应用所需的全部内容 (适用于所有设备) 都涵盖在内:所有语言、所有设备屏幕大小、所有硬件架构。接着,在用户下载您的应用时,Google Play 的新动态交付只会传输适用于用户设备的代码和资源。人们在 Play Store 上看到的安装包体积更小,下载速度也越快,同时也节省了设备存储空间。

结合Google Play Dynamic Delivery (动态交付) , 实现动态功能
Android App Bundle 支持模块化,通过Dynamic Delivery with split APKs,将一个apk拆分成多个apk,按需加载(包括加载C/C++ libraries),这样开发者可以随时按需交付功能,而不是仅限在安装过程中。

  • Base Apk
    首次安装的apk,公共代码和资源,所以其他的模块都基于Base Apk
  • Configuration APKs
    native libraries 和适配当前手机屏幕分辨率的资源
  • Dynamic feature APKs
    不需要在首次安装就加载的模块

使用方法

app模块为Base Apk,features目录下的模块为Dynamic feature APKs。Base Apk和正常的Android应用项目一样,它决定了最终程序的版本号、应用签名、代码缩小、splits、包名等信息。

默认情况下,在构建应用程序包时,它支持为每组语言资源,屏幕密度资源和ABI库生成配置APK。使用android.bundle基本模块build.gradle文件中的块 ,如下所示,您可以禁用对一种或多种配置APK的支持:

android {
    // When building Android App Bundles, the splits block is ignored.
    splits {...}

    // Instead, use the bundle block to control which types of configuration APKs
    // you want your app bundle to support.
    bundle {
        language {
            // Specifies that the app bundle should not support
            // configuration APKs for language resources. These
            // resources are instead packaged with each base and
            // dynamic feature APK.
            enableSplit = false
        }
        density {
            // This property is set to true by default.
            enableSplit = true
        }
        abi {
            // This property is set to true by default.
            enableSplit = true
        }
    }
}

Dynamic feature APKs是需要动态加载的模块,如果手机不支持动态加载,其内容会合并到最终的apk内。

创建动态功能模块

创建新动态功能模块的最简单方法是使用Android Studio 3.2或更高版本。由于动态要素模块对基础应用程序模块具有内在依赖性,因此您只能将它们添加到现有应用程序项目中。

要使用Android Studio向您的应用项目添加动态功能模块,请执行以下操作:

  1. 如果您还没有这样做,请在IDE中打开您的应用程序项目。
  2. 从菜单栏中选择File> New> New Module。
  3. 在Create New Module对话框中,选择 Dynamic Feature Module,然后单击Next。
  4. 在“ 配置新模块”部分中,完成以下操作:
    a. 从下拉菜单中选择应用程序项目的Base应用程序模块。
    b. 指定模块名称。IDE使用此名称将模块标识为Gradle设置文件中的Gradle子项目 。构建应用程序包时,Gradle使用子项目名称的最后一个元素将<manifest split>属性 注入AndroidManifest清单中。
    c. 指定模块的包名称。默认情况下,Android Studio会建议一个包名称,该名称包含基本模块的根包名称和您在上一步中指定的模块名称。
    d. 选择希望模块支持的最低API级别。该值应与基本模块的值匹配。
  5. 单击下一步。
  6. 在“ 配置按需选项”部分中,执行如下操作:
    a. 使用最多50个字符指定模块标题。例如,当确认用户是否想要下载模块时,平台使用该标题来向用户标识模块。因此,您的应用程序的基本模块必须包含模块标题作为字符串资源,您可以将其翻译。使用Android Studio创建模块时,IDE会为您将字符串资源添加到基本模块,并在动态要素模块的清单中注入以下条目:
<dist:module
    ...
    dist:title="@string/title_dynamic_feature">
</dist:module>

b. 如果希望模块可用于按需下载,请选中“ 按需启用 ”旁边的框。如果您未启用此选项,则当用户首次下载并安装您的应用时,可以使用动态功能。Android Studio会在模块的清单中注入以下内容以反映您的选择。

<dist:module
    ...
    dist:onDemand="true">
</dist:module>

c. 如果您希望此模块可供运行Android 4.4(API级别20)及更低版本的设备使用,请选中“ 融合 ”旁边的框,并将其包含在多个APK中。仅当您选中上一步中按需启用旁边的框时,此选项才可用。这意味着您可以为此模块启用按需行为,并禁用融合以从不支持下载和安装拆分APK的设备中省略它。Android Studio会在模块的清单中注入以下内容以反映您的选择。

<dist:module
    ...
    <dist:fusing dist:include="true" />
</dist:module>
  1. 单击完成。
    在Android Studio完成模块创建后,请从“ 项目”窗格中自行检查其内容( 从菜单栏中选择“ 视图”>“工具窗口”>“项目 ”)。您应该快速注意到默认代码,资源和组织与标准应用程序模块的默认代码,资源和组织类似。

动态功能模块构建配置

使用Android Studio创建新的动态要素模块时,IDE会将以下Gradle插件应用于模块的build.gradle文件。

// The following applies the dynamic-feature plugin to your dynamic feature module.// The plugin includes the Gradle tasks and properties required to configure and build// an app bundle that includes your dynamic feature module.apply plugin: 'com.android.dynamic-feature'

什么不包括在动态功能模块构建配置中

由于每个动态功能模块都依赖于基本模块,因此它还会继承某些配置。因此,您应该在动态要素模块的build.gradle文件中省略以下内容:

  • 签名配置:使用您在基本模块中指定的签名配置对应用程序包进行签名。
  • minifyEnabled属性:您可以 使代码萎缩 仅从基本模块的构建配置为您的整个应用项目。因此,您应该从动态功能模块中省略此属性。但是,您可以 为每个动态要素模块指定其他ProGuard规则
  • versionCodeversionName:构建应用程序包时,Gradle使用基本模块提供的应用程序版本信息。您应该从动态模块的build.gradle文件中省略这些属性 。

建立与基础模块的关系

当Android Studio创建动态要素模块时,通过将android.dynamicFeatures属性添加到基础模块的build.gradle文件,它使基本模块可见,如下所示:

// In the base module’s build.gradle file.android {    ...    // Specifies dynamic feature modules that have a dependency on    // this base module.    dynamicFeatures = [":dynamic-feature", ":dynamic-feature2"]}
此外,Android Studio将基本模块作为动态功能模块的依赖项,如下所示:
// In the dynamic feature module’s build.gradle file:...dependencies {    ...    // Declares a dependency on the base module, ':app'.    implementation project(':app')}

指定其他ProGuard规则

虽然只有基本模块的构建配置可以为您的应用项目启用代码缩减,但可以使用proguardFiles属性为每个动态功能模块提供自定义ProGuard规则 ,如下所示。

android.buildTypes {
     release {
         // You must use the following property to specify additional ProGuard
         // rules for dynamic feature modules.
         proguardFiles 'proguard-rules-dynamic-features.pro'
     }
}

部署您的应用

在开发支持动态交付的应用程序时,可以像往常一样通过从菜单栏选择“运行”>“运行”(或单击工具栏中的“运行 ),将应用程序部署到连接的设备 。

附:谷歌官方示例 https://github.com/googlesamples/android-dynamic-features

When bundletool generates APKs from your app bundle, it includes them in a container called an APK set archive, which uses the .apks file extension. To generate an APK set for all device configurations your app supports from your app bundle, use the bundletool build-apks command, as shown below.

bundletool build-apks --bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks

Note that the command above creates an APK set of unsigned APKs. If you want to deploy the APKs to a device, you need to also include your app’s signing information, as shown in the command below.

bundletool build-apks --bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks
--ks=/MyApp/keystore.jks
--ks-pass=file:/MyApp/keystore.pwd
--ks-key-alias=MyKeyAlias
--key-pass=file:/MyApp/key.pwd

If you’d rather not build a set of APKs for all device configurations your app supports, you can build APKs that target only the configuration of a connected device using the --connected-device option, as shown below. (If you have multiple devices connected, specify a target device by including the --device-id=serial-id flag.)

bundletool build-apks --connected-device
--bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks

Generate and use device specification JSON files

bundletool is capable of generating an APK set that targets a device configuration specified by a JSON file. To first generate a JSON file for a connected device, run the following command:

bundletool get-device-spec --output=/tmp/device-spec.json

bundletool creates a JSON file for your device in the directory the tool is located. You can then pass it to bundletool to generate a set of APKs that target only the configuration described in that JSON file as follows:

bundletool build-apks --device-spec=/MyApp/pixel2.json
--bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks

To measure the estimated download sizes of APKs in an APK set as they would be served compressed over-the-wire, use the get-size total command:

bundletool get-size total --apks=/MyApp/my_app.apks

 总结命令:

//1

/Users/edz/Library/Android/sdk/platform-tools/adb

/Users/edz/buildtool/aab/specs.json

java -jar /Users/edz/buildtool/bundletool-all-0.9.0.jar  get-device-spec --adb=/Users/edz/Library/Android/sdk/platform-tools/adb  --output=/Users/edz/buildtool/aab/specs.json

//2

java -jar /Users/edz/buildtool/bundletool-all-0.9.0.jar  build-apks --connected-device --adb=/Users/edz/Library/Android/sdk/platform-tools/adb --bundle=/Users/edz/buildtool/aab/app.aab  --output=/Users/edz/buildtool/aab/app.apks

//3

java -jar /Users/edz/buildtool/bundletool-all-0.9.0.jar  install-apks --apks=/Users/edz/buildtool/aab/app.apks --adb=/Users/edz/Library/Android/sdk/platform-tools/adb  

发布了74 篇原创文章 · 获赞 36 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/kdsde/article/details/91041086