Google aab格式的政策解读

706 阅读10分钟

1. 背景

Google Play在今年3月发出了一个 Google Play新政策通知,即在今年8月后新应用必须以 API 级别 30 (Android 11) 为目标平台,并使用 Android App Bundle(aab)发布格式,对于现有应用是不受强制影响的。

首先声明一点: aab是开发者上传至应用市场的应用包格式,不是手机解析安装的格式,目的是为了用户用更少流量下载到机型匹配的apk包,同时开发者也能更放心的使用多abi架构的so库,不用担心so兼容对apk大小的影响,很多媒体带节奏发新闻说aab是代替apk格式,目的是针对华为或者国内手机厂商,纯属扯淡,简直离离原上谱 如果我没记错的话,早在18年Google就已经提出了aab这个东西。

image.png

那么对于这次提到的Android APP Bundle直接带来的好处也是清晰明了的,我直接给撸过来了:

image.png

Google官方说平均减少包体积15%,对于使用很多so库的应用这个数字还是太过保守,据我实际测试下来so库占用apk总体积的53.8%情况下,aab格式比apk格式的分发下载直接减小了25%,别的不说就光从这一点上面就已经很有吸引力了。

2. .aab的本质

.aab实际上和.apk一样是个压缩包,你可以直接改后缀为.zip然后解压,解压后有如下目录:

image.png

其中base里是一些主要资源,.pb的文件是一些描述,如下:

image.png

aab文件将语言、分辨率、CPU架构、图片尺寸等拆分成N个.apk,例如下,可以看到有87个apk文件

image.png

再根据设备软硬件情况组合成一个size小很多的apk进行安装。比如在google play上,aab会被打成N个apk再组合分发到对应的设备上。

3. aab如何安装到手机上

.aab不能直接安装到设备上,需要通过工具命令把它转成.apks,再使用工具命令组合多个apk部署到设备(这个过程是工具内部根据adb连接的设备自动组合的)。操作.aab格式的文件,需要下载官方工具bundletool

  • 转aab为apks命令: build-apks // --bundle为输入文件的全路径(当前目录直接使用文件名) --output为输出文件全路径 bundletool build-apks --bundle=my.aab --output=my.apks 直接使用如上命令应该是会报错找不到命令的command not found: bundletool,正确执行jar文件方式是到bundletool.jar的文件夹下或者使用全路径

java -jar bundletool-xxx.jar build-apks --bundle=my.aab --output=my.apks ​ 以上方式仅适合在项目目录下运行,因为这个过程其实是需要签名文件和相关参数的,默认情况下会按照未二次签名的debug版本处理,下面会介绍到带参数转aab

  • 安装命令: install-apks

    java -jar bundletool-xxx.jar install-apks --apks=myapp.apks

  • 带参数转aab为apks: build-apks

    java -jar bundletool-xxx.jar build-apks --bundle=my.aab --output=my.apks --ks=/path/xxx.jks --ks-pass=pass:xxx --ks-key-alias=xxx --key-pass=pass:xxx

4. split apk机制

4.1 背景

虽然您应尽可能构建一个 APK 来支持所有目标设备,但由于需要许多文件来支持多种屏幕密度应用二进制接口 (ABI),因此这样做可能会导致生成非常大的 APK。如需减小 APK 的大小,一种方法是创建多个 APK,让每个 APK 包含适用于特定屏幕密度或 ABI 的文件。

Gradle 可以创建单独的 APK,让每个 APK 仅包含每种密度或 ABI 专用的代码和资源。本页介绍了如何配置 build 以生成多个 APK。如果您需要创建不基于屏幕密度或 ABI 的不同应用版本,您可以改用 build 变体

4.2 针对屏幕密度配置多个 APK

如需针对不同的屏幕密度创建单独的 APK,请在 splits 代码块内添加一个 density 代码块。在 density 代码块中,提供所需屏幕密度和兼容屏幕尺寸的列表。只有在每个 APK 的清单中需要特定 `` 元素时,才应使用兼容屏幕尺寸的列表。

以下 Gradle DSL 选项用于针对屏幕密度配置多个 APK:

  • enable

    如果将此元素设为 true,Gradle 会根据您定义的屏幕密度生成多个 APK。默认值为 false

  • exclude

    以逗号分隔列表的形式指定 Gradle 不应针对哪些密度生成单独的 APK。如果您要针对大多数密度生成 APK,但需要排除您的应用不支持的一些密度,请使用 exclude

  • reset()

    清空默认的屏幕密度列表。只与 include 元素结合使用,以指定您想要添加的密度。以下代码段将密度列表设为仅包含 ldpixxhdpi,方法是先调用 reset() 以清空该列表,然后再使用 includereset() // Clears the default list from all densities to no densities. include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.

  • include

    以逗号分隔列表的形式指定 Gradle 应针对哪些密度生成 APK。只与 reset() 结合使用,以指定确切的密度列表。

  • compatibleScreens

    以逗号分隔列表的形式指定兼容的屏幕尺寸。此元素将在每个 APK 的清单中注入一个匹配的 ](https://developer.android.com/guide/topics/manifest/compatible-screens-element?authuser=2) 节点。此设置提供了一种便捷的方法,让您可以在 `build.gradle` 文件中的同一部分管理屏幕密度和屏幕尺寸。不过,使用 [ 可能会限制您的应用支持的设备类型。如需了解支持不同屏幕尺寸的其他方法,请参阅支持多种屏幕

4.3 针对 ABI 配置多个 APK

如需针对不同的 ABI 创建单独的 APK,请在 splits 代码块内添加一个 abi 代码块。在 abi 代码块中,提供所需 ABI 的列表。

以下 Gradle DSL 选项用于按 ABI 配置多个 APK:

  • enable

    如果将此元素设为 true,Gradle 会根据您定义的 ABI 生成多个 APK。默认值为 false

  • exclude

    以逗号分隔列表的形式指定 Gradle 不应针对哪些 ABI 生成单独的 APK。如果您要针对大多数 ABI 生成 APK,但需要排除您的应用不支持的一些 ABI,请使用 exclude

  • reset()

    清空默认的 ABI 列表。只与 include 元素结合使用,以指定您想要添加的 ABI。以下代码段将 ABI 列表设为仅包含 x86x86_64,方法是先调用 reset() 以清空该列表,然后再使用 includereset() // Clears the default list from all ABIs to no ABIs. include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.

  • include

    以逗号分隔列表的形式指定 Gradle 应针对哪些 ABI 生成 APK。只与 reset() 结合使用,以指定确切的 ABI 列表。

  • universalApk

    如果设为 true,那么除了按 ABI 生成的 APK 之外,Gradle 还会生成一个通用 APK。通用 APK 将适用于所有 ABI 的代码和资源包含在一个 APK 中。 默认值为 false。请注意,此选项仅在 splits.abi 代码块中可用。根据屏幕密度构建多个 APK 时,Gradle 始终会生成一个包含适用于所有屏幕密度的代码和资源的通用 APK。

以下示例针对每个 ABI(即 x86x86_64)生成了单独的 APK,方法是先使用 reset() 清空 ABI 列表,然后再使用 include 添加一些 ABI(它们将各自获得一个 APK)。

android {
  ...
  splits {
​
    // Configures multiple APKs based on ABI.
    abi {
​
      // Enables building multiple APKs per ABI.
      enable true
​
      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86 and x86_64.
​
      // Resets the list of ABIs that Gradle should create APKs for to none.
      reset()
​
      // Specifies a list of ABIs that Gradle should create APKs for.
      include "x86", "x86_64"
​
      // Specifies that we do not want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

4.4 配置版本控制

默认情况下,当 Gradle 生成多个 APK 时,每个 APK 都有相同的版本信息,该信息在模块级 build.gradle 文件中指定。由于 Google Play 商店不允许同一个应用的多个 APK 全都具有相同的版本信息,因此在上传到 Play 商店之前,您需要确保每个 APK 都有自己唯一的 versionCode

您可以配置模块级 build.gradle 文件以替换每个 APK 的 versionCode。通过创建一种映射关系来为您配置了多 APK 构建的每种 ABI 和密度分配一个唯一的数值,您可以将输出版本代码替换为一个将在 defaultConfigproductFlavors 代码块中定义的版本代码与分配给相应密度或 ABI 的数值组合在一起的值。

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}
​
// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]
​
import com.android.build.OutputFile// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->
​
  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->
​
    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
​
    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {
​
      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

4.5 构建多个apk

将模块级 build.gradle 文件配置为构建多个 APK 后,依次点击 Build > Build APK,为 Project 窗格中当前选定的模块构建所有 APK。Gradle 会在项目的 build/outputs/apk/ 目录中针对每种密度或 ABI 创建 APK。

Gradle 会针对您配置了多 APK 构建的每种密度或 ABI 构建一个 APK。如果启用了同时针对密度和 ABI 的多 APK 构建,Gradle 会针对密度和 ABI 的每个组合创建一个 APK。例如,以下 build.gradle 代码段启用了针对 mdpi 和 hdpi 密度以及 x86 和 x86_64 ABI 的多 APK 构建。

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

上述示例配置的输出包括以下 4 个 APK:

  • app-hdpiX86-release.apk:仅包含适用于 hdpi 密度和 x86 ABI 的代码和资源。
  • app-hdpiX86_64-release.apk:仅包含适用于 hdpi 密度和 x86_64 ABI 的代码和资源。
  • app-mdpiX86-release.apk:仅包含适用于 mdpi 密度和 x86 ABI 的代码和资源。
  • app-mdpiX86_64-release.apk:仅包含适用于 mdpi 密度和 x86_64 ABI 的代码和资源。

4.6 文件名输出

构建多个 APK 时,Gradle 使用的 APK 文件名采用以下方案:

modulename-screendensityABI-buildvariant.apk

该方案由下面几部分组成:

  • modulename

    指定正在构建的模块名称。

  • screendensity

    如果启用了针对屏幕密度的多 APK 构建,应指定 APK 对应的屏幕密度,如“mdpi”。

  • ABI

    如果启用了针对 ABI 的多 APK 构建,应指定 APK 对应的 ABI,如“x86”。如果启用了同时针对屏幕密度和 ABI 的多 APK 构建,Gradle 会将密度名称与 ABI 名称连接起来,例如“mdpiX86”。如果针对按 ABI 生成的 APK 启用了 universalApk,Gradle 会将“universal”用作通用 APK 文件名的 ABI 部分。

  • buildvariant

    指定正在构建的 build 变体,如“debug”。

4.7 Demo输出认证

4.7.1 gradle配置选项设置

android{
  splits {
    density {
        enable true
        reset()
        include "mdpi", "hdpi"
    }
    abi {
        enable true
        reset()
        include "x86"
    }
  }
}

4.7.2 编译demo结果

image.png

5. Android Studio打包认证aab格式包

5.1 AS的设置选项

  1. 打开Android Studio工程,打开Build-Generate Singned Bundle/APK...选项

    注意:该选项上面的Build Bundles(s)/APK(s)打包出来的是没有签名的,无法上传到后台,应使用该选项上传。

image.png

  1. 下一步操作

image.png

  1. 选择签名

    注意:需要勾选Export encrypted key for enrolling published apps in Google Play App Signing,该导出的私钥文件上传到后台时会用到。

image.png

  1. 选择打包类型,点击finish

image.png

  1. 打包完成会在release目录下看到aab文件,上传后台即可

image.png