bundletool

bundletool 是 Android Studio、Android Gradle 插件和 Google Play 用于构建 Android 应用捆绑包的底层工具。 bundletool 可以将应用捆绑包转换为部署到设备的各种 APK。

Android SDK 捆绑包 (ASB) 及其 APK 使用 bundletool 构建。它也作为命令行工具提供,因此您可以自己构建应用捆绑包和 SDK 捆绑包,并重新创建 Google Play 对您的应用程序 APK 或您 启用运行时的 SDK 的 APK 的服务器端构建。

下载 bundletool

如果您尚未下载,请从 GitHub 代码库 下载 bundletool

构建和测试应用捆绑包

您可以使用 Android Studio 或 bundletool 命令行工具构建 Android 应用捆绑包,然后测试从此应用捆绑包生成 APK。

构建应用捆绑包

使用 Android Studio 和 Android Gradle 插件 构建并签名 Android 应用捆绑包。但是,如果无法使用 IDE(例如,因为您使用的是持续构建服务器),您也可以 从命令行构建您的应用捆绑包 并使用 jarsigner 为其签名。

有关使用 bundletool 构建应用捆绑包的更多信息,请参阅 使用 bundletool 构建应用捆绑包

从您的应用捆绑包生成一组 APK

构建 Android 应用捆绑包后,测试 Google Play 如何使用它生成 APK 以及这些 APK 部署到设备后的行为。

您可以通过两种方式测试应用捆绑包

本部分介绍如何使用 bundletool 在本地测试您的应用捆绑包。

bundletool 从您的应用捆绑包生成 APK 时,它会将生成的 APK 包含在一个名为“APK 集归档文件”的容器中,该容器使用 .apks 文件扩展名。要从您的应用捆绑包为您的应用程序支持的所有设备配置生成 APK 集,请使用 bundletool build-apks 命令,如以下所示

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

如果要将 APK 部署到设备,您还需要包含应用程序的签名信息,如以下命令所示。如果您未指定签名信息,bundletool 会尝试使用调试密钥为您签名 APK。

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

下表详细介绍了在使用 bundletool build-apks 命令时可以设置的各种标志和选项

表 1. bundletool build-apks 命令的选项

标志 描述
--bundle=path (必填) 指定使用 Android Studio 构建的应用捆绑包的路径。要了解更多信息,请阅读 构建您的项目
--output=path (必填) 指定包含应用程序所有 APK 工件的输出 .apks 文件的名称。要在设备上测试此文件中的工件,请按照关于如何 将 APK 部署到已连接的设备 一节中的步骤操作。
--overwrite 使用 --output 选项指定的路径覆盖任何现有的输出文件。如果您不包含此标志且输出文件已存在,则会收到构建错误。
--aapt2=path 指定 AAPT2 的自定义路径。默认情况下,bundletool 包含它自己的 AAPT2 版本。
--ks=path (可选) 指定用于签名 APK 的部署密钥库的路径。如果您不包含此标志,bundletool 会尝试使用调试签名密钥签名 APK。
--ks-pass=pass:password

--ks-pass=file:/path/to/file
指定您的密钥库密码。如果您以纯文本形式指定密码,请使用 pass: 限定它。如果您传递包含密码的文件的路径,请使用 file: 限定它。如果您使用 --ks 标志指定密钥库,但未指定 --ks-pass,则 bundletool 会提示您从命令行输入密码。
--ks-key-alias=alias 指定要使用的签名密钥的别名。
--key-pass=pass:password

--key-pass=file:/path/to/file
指定签名密钥的密码。如果您以纯文本形式指定密码,请在前面加上 pass:。如果您传递包含密码的文件的路径,请在前面加上 file:

如果此密码与密钥库本身的密码相同,您可以省略此标志。

--connected-device 指示 bundletool 构建针对连接设备配置的 APK。如果您不包含此标志,bundletool 将为您的应用支持的所有设备配置生成 APK。
--device-id=serial-number 如果您有多个连接的设备,请使用此标志指定要部署应用的设备的序列 ID。
--device-spec=spec_json 提供指向 .json 文件的路径,该文件指定您要定位的设备配置。要了解更多信息,请转到有关如何 生成和使用设备规范 JSON 文件 的部分。
--mode=universal 将模式设置为 universal。如果您希望 bundletool 构建一个包含应用所有代码和资源的单个 APK,以便 APK 与应用支持的所有设备配置兼容,请使用此选项。

注意: bundletool 在通用 APK 中仅包含在其清单中指定 <dist:fusing dist:include="true"/> 的功能模块。要了解更多信息,请阅读有关 功能模块清单 的内容。

请记住,这些 APK 比针对特定设备配置优化的 APK 大。但是,它们更容易与内部测试人员共享,例如,内部测试人员想要在多个设备配置上测试您的应用。

--local-testing 为您的应用捆绑包启用本地测试。本地测试允许进行快速、迭代的测试周期,而无需上传到 Google Play 服务器。

有关如何使用 --local-testing 标志测试模块安装的示例,请参阅 本地测试模块安装

将 APK 部署到连接的设备

生成一组 APK 后,bundletool 可以从该集中将正确的 APK 组合部署到连接的设备。

例如,如果您有一个运行 Android 5.0(API 级别 21)或更高版本的连接设备,bundletool 将推送在该设备上运行您的应用所需的基 APK、功能模块 APK 和配置 APK。或者,如果您的连接设备运行的是 Android 4.4(API 级别 20)或更低版本,bundletool 将搜索要部署到您的设备的兼容多 APK。

要从 APK 集部署应用,请使用 install-apks 命令并使用 --apks=/path/to/apks 标志指定 APK 集的路径,如以下命令所示。如果您有多个连接的设备,请通过添加 --device-id=serial-id 标志指定目标设备。

bundletool install-apks --apks=/MyApp/my_app.apks

生成特定于设备的 APK 集

如果您不想为您的应用支持的所有设备配置构建一组 APK,您可以使用 --connected-device 选项构建仅针对连接设备配置的 APK,如以下命令所示。如果您有多个连接的设备,请通过包含 --device-id=serial-id 标志指定目标设备。

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

生成和使用设备规范 JSON 文件

bundletool 可以生成一组 APK,这些 APK 针对 JSON 文件指定的设备配置。要首先为连接的设备生成 JSON 文件,请运行以下命令

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

bundletool 在工具的目录中创建您的设备的 JSON 文件。然后,您可以将该文件传递给 bundletool,以生成仅针对该 JSON 文件中描述的配置的一组 APK,如下所示

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

手动创建设备规范 JSON

如果您无法访问要为其构建目标 APK 集的设备,例如,如果您想使用您手头没有的设备试用您的应用,您可以使用以下格式手动创建一个 JSON 文件

{
  "supportedAbis": ["arm64-v8a", "armeabi-v7a"],
  "supportedLocales": ["en", "fr"],
  "screenDensity": 640,
  "sdkVersion": 27
}

然后,您可以将此 JSON 传递给 bundle extract-apks 命令,如上一节所述。

从现有 APK 集中提取特定于设备的 APK

如果您有一个现有的 APK 集,并且您想从中提取针对特定设备配置的 APK 子集,您可以使用 extract-apks 命令并指定设备规范 JSON,如下所示

bundletool extract-apks
--apks=/MyApp/my_existing_APK_set.apks
--output-dir=/MyApp/my_pixel2_APK_set.apks
--device-spec=/MyApp/bundletool/pixel2.json

测量 APK 集中 APK 的预计下载大小

要测量 APK 集中 APK 的预计下载大小(如通过网络压缩后提供的那样),请使用 get-size total 命令

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

您可以使用以下标志修改 get-size total 命令的行为

表 2. get-size total 命令的选项

标志 描述
--apks=path (必需) 指定要测量下载大小的现有 APK 集文件的路径。
--device-spec=path 指定设备规范文件(来自 get-device-spec 或手动构建)的路径,用于匹配。您可以指定部分路径以评估一组配置。
--dimensions=dimensions 指定计算大小估算时使用的维度。接受逗号分隔的列表:SDKABISCREEN_DENSITYLANGUAGE。要测量所有维度,请指定 ALL
--instant 测量即时启用 APK 的下载大小,而不是可安装 APK 的下载大小。默认情况下,bundletool 测量可安装 APK 的下载大小。
--modules=modules 指定 APK 集中要考虑在测量中的模块的逗号分隔列表。bundletool 命令会自动包含指定集的任何依赖模块。默认情况下,该命令会测量第一次下载期间安装的所有模块的下载大小。

使用 SDK 捆绑包依赖项构建应用捆绑包(实验性)

您可以使用命令行构建具有 Android SDK 捆绑包 (ASB) 依赖项的 Android 应用捆绑包,并使用 jarsigner 对其进行签名。

每个应用捆绑包模块都包含一个模块协议缓冲区 (.pb) 文件:runtime_enabled_sdk_config.pb。此文件包含应用捆绑包模块依赖的 SDK 列表。有关此文件的完整定义,请参阅 runtime_enabled_sdk_config.proto 文件。

要构建具有 SDK 捆绑包依赖项的应用捆绑包,请按照有关 使用 bundletool 构建应用捆绑包 部分中的步骤操作,并将 runtime_enabled_sdk_config.pb 文件添加到每个应用模块的 zip 文件中,该文件包含已编译的代码和资源。

runtime_enabled_sdk_config.pb 文件中一些值得注意的字段

  • 证书摘要:用于对 SDK 的 APK 进行签名的密钥的证书的 SHA-256 摘要。这对应于 Android SDK 归档格式SdkMetadata.pb 文件中的证书。

  • 资源包 ID:在生成用于将 SDK 嵌入应用的 APK 时,此 SDK 中的所有资源都将重新映射到的包 ID。这使得向后兼容成为可能。

SDK 只能出现在一个模块中。如果多个模块依赖于同一个 SDK,则应对该依赖项进行重复数据删除并将其移至基模块。不同的模块不能依赖于 SDK 的不同版本。

从具有 SDK 捆绑包依赖项的应用捆绑包中生成 APK(实验性)

要从您的应用捆绑包中生成 APK,请按照有关 从您的应用捆绑包中生成一组 APK 的部分或有关 生成特定于设备的 APK 集 的部分中的步骤操作,并向 bundletool build-apks 命令提供应用依赖的 SDK。这些 SDK 可以以 SDK 捆绑包格式或 SDK 归档格式提供。

您可以通过添加 --sdk-bundles 标志以 SDK 捆绑包的形式提供 SDK,如下所示

bundletool build-apks --bundle=app.aab --sdk-bundles=sdk1.asb,sdk2.asb \
    --output=app.apks

您可以通过添加 --sdk-archives 标志以 SDK 归档的形式提供 SDK,如下所示

bundletool build-apks --bundle=app.aab --sdk-archives=sdk1.asar,sdk2.asar \
    --output=app.apks
为不支持 SDK 库的设备从具有 SDK 捆绑包依赖项的应用捆绑包中生成 APK

Android 13 之前的设备不支持安装 SDK 库或在 SDK 运行时运行它们。Bundletool 隐藏了向后兼容的复杂性,当您使用 --sdk-bundles--sdk-archives 选项运行 bundletool build-apks 时,会从同一个应用捆绑包中生成您的 APK 集的多个变体。多个变体针对具有不同功能的设备

  • 对于较新的设备,存在一个变体,其中 SDK 作为与应用不同的软件包安装,并且应用 APK 不包含任何 SDK 内容。
  • 对于较旧的设备,存在一个或多个变体,其中 SDK APK 被添加到应用 APK 集中作为额外的 APK 拆分。SDK APK 属于应用包。在这种情况下,SDK 运行时在设备上的应用运行时中被模拟。

与为没有 SDK 依赖项的应用捆绑包生成 APK 的方式类似,bundletool extract-apksbundletool install-apks 会从连接设备或提供的设备配置中最合适的变体中返回一组经过过滤的 APK。

对于高级用例,如果您只对为较旧设备的特定应用从 SDK 归档中生成 APK 拆分感兴趣,请使用 bundletool build-sdk-apks-for-app 命令,如下所示

bundletool build-sdk-apks-for-app --app-properties=app-properties.json \
    --sdk-archive=sdk.asar --output=sdk.apks

app-properties 文件应包含 runtime_enabled_sdk_config.proto 文件中描述的字段。这是 app-properties 文件的外观

{
  "package_name": "com.my.app",
  "version_code": 1234,
  "min_sdk_version": 21,
  "resources_package_id": 0x7e
}

bundletool build-sdk-apks-for-app 命令会生成与应用包名称下的 SDK 内容相对应的应用 APK 子集。您可以将这些 APK 与包含应用内容的其他 APK 结合使用。例如,如果您分别构建它们并以增量方式构建它们,并在不支持 SDK 运行时的设备上一起安装它们。

构建和测试 SDK 捆绑包(实验性)

您可以使用 bundletool 构建 ASB 并测试生成安装和分发所需的文件。

构建 SDK 捆绑包

您可以使用命令行构建 ASB 并使用 jarsigner 对其进行签名。

要构建 SDK 捆绑包,请按照以下步骤操作

  1. 以 proto 格式生成 SDK 捆绑包的清单和资源,方法是按照与应用捆绑包相同的步骤操作。

  2. 将 SDK 的已编译代码和资源打包到一个基 zip 文件中,就像您对应用模块所做的那样。

  3. 生成一个 SdkModulesConfig.pb.json 文件和一个 SdkBundleConfig.pb.json 文件,格式与 Android SDK Bundle 规范 中描述的格式匹配。

  4. 使用 bundletool build-sdk-bundle 命令构建您的 ASB,如下所示

bundletool build-sdk-bundle --sdk-bundle-config=SdkBundleConfig.pb.json \
    --sdk-modules-config=SdkModulesConfig.pb.json \
    --modules=base.zip --output=sdk.asb

下表详细描述了在使用 bundletool build-sdk-bundle 命令时可以设置的各种标志和选项。

表 3. bundletool build-sdk-bundle 命令的选项

标志 描述
--modules (必需) 您要从中构建最终 ASB 的模块文件。
--output (必需) 您希望 ASB 构建到的路径。
--sdk-modules-config (必需) 描述 SDK 模块配置的 JSON 文件的路径。要了解如何格式化 JSON 文件,请参阅 Android SDK Bundle 规范 部分。
--sdk-bundle-config 描述 SDK 包配置的 JSON 文件的路径。要了解如何格式化 JSON 文件,请参阅 Android SDK Bundle 规范 部分。
--metadata-file 用于在 ASB 中包含元数据的文件。标志值的格式为 <bundle-path>:<physical-file>,其中 <bundle-path> 表示 SDK 包元数据目录内的文件位置,而 <physical-file> 是包含要存储的原始数据的现有文件。该标志可以重复使用。
--overwrite 如果设置,此选项将覆盖任何先前存在的输出。

从 SDK 包中生成 APK

构建 ASB 后,您可以通过使用 bundletool build-sdk-apks 命令生成其 APK 来在本地测试 SDK 包,如以下代码所示

bundletool build-sdk-apks --sdk-bundle=sdk.asb --output=sdk.apks

bundletool 从您的 SDK 包中生成 APK 时,该工具将 APKS 包含在一个名为APK 集成存档的容器中,该容器使用 .apks 文件扩展名。 bundletool 从 SDK 包中生成单个独立的 APK,该 APK 针对所有设备配置。

如果要将 ASB 部署到设备,则还需要包含您的应用程序的签名信息,如以下命令所示

bundletool build-sdk-apks --sdk-bundle=sdk.asb --output=sdk.apks \
    --ks=keystore.jks \
    --ks-pass=file:/keystore.pwd \
    --ks-key-alias=KeyAlias \
    --key-pass=file:/key.pwd

下表详细描述了在使用 bundletool build-sdk-apks 命令时可以设置的各种标志和选项。

表 4. bundletool build-sdk-apks 命令的选项

标志 描述
--sdk-bundle (必需) SDK 包的路径。必须具有扩展名 .asb
--output (必需) 默认情况下,您要创建 APK 集成存档的路径。或者,如果您使用 --output-format=DIRECTORY,则这是您要存储生成 APK 的目录的路径。
--ks 您要用于签名生成 APK 的密钥库的路径。
--ks-key-alias 密钥库中用于签名生成 APK 的密钥的别名。
--key-pass

密钥库中用于签名生成 APK 的密钥的密码。

如果您以明文形式传递密码,则必须在值前面加上 pass:。例如,pass:qwerty。如果密码是文件的首行,则必须在值前面加上 file:。例如,file:/tmp/myPassword.txt

如果没有设置此标志,则会尝试使用密钥库密码。如果失败,命令行终端会提示您输入密码。

--ks-pass

用于签名生成 APK 的密钥库的密码。

如果您以明文形式传递密码,则必须在值前面加上 pass:。例如,pass:qwerty。如果密码是文件的首行,则必须在值前面加上 file:。例如,file:/tmp/myPassword.txt

如果没有设置此标志,命令行终端会提示您输入密码。

--aapt2 要使用的 AAPT2 二进制文件的路径。
--output-format 生成 APK 的输出格式。默认情况下,此选项设置为 APK_SET,它将 APK 输出到创建的 APK 集成存档中。如果设置为 DIRECTORY,它将 APK 输出到 --output 指定的目录中。
--verbose 如果设置,此选项将在标准输出中打印有关命令执行的额外信息。
--version-code SDK 版本代码。这是 Android 平台用于安装 APK 的版本代码,而不是 SDK 版本。此选项可以设置为任意值。如果没有设置,它默认为 0。
--overwrite 如果设置,此选项将覆盖任何先前存在的输出。

部署、提取和衡量 SDK APK 的大小

您可以按照与应用程序相同的步骤 将 APK 部署到已连接的设备从现有 APK 集成中提取设备特定的 APK 以及 衡量 APK 集成中 APK 的估计下载大小

从 SDK 包中生成 SDK 存档

将 ASB 上传到分发渠道(例如 Google Play)后,ASB 会转换为 Android SDK 存档 (.asar),以便通过 Maven 分发给应用程序开发人员。有关格式的更多详细信息,请参阅有关 SDK 存档格式规范 的部分。

构建 ASB 后,您可以使用 bundletool build-sdk-asar 命令在本地测试 Android SDK 存档的生成,如以下代码所示

bundletool build-sdk-asar --sdk-bundle=sdk.asb --output=sdk.asar \
    --apk-signing-key-certificate=keycert.txt

下表详细描述了在使用 bundletool build-sdk-asar 命令时可以设置的各种标志和选项。

表 5. bundletool build-sdk-asar 命令的选项

标志 描述
--apk-signing-key-certificate (必需) SDK APK 签名证书的路径。这是与您用于在 build-sdk-apks 命令中签名 APK 的密钥相对应的证书。
--output (必需) 您要创建 .asar 文件的路径。
--sdk-bundle (必需) SDK 包的路径。必须具有扩展名 .asb
--overwrite 如果设置,此选项将覆盖任何先前存在的输出。

运行时启用 SDK 格式(实验性)

运行时启用 SDK 引入了两种 Android 文件格式

Android SDK Bundle 格式

SDK Bundle 是运行时启用 SDK 的发布格式。它包含所有 SDK 代码和资源,包括 SDK 依赖的任何库的代码。它不包括 SDK 依赖的其他运行时启用 SDK 的代码和资源。

Android SDK Bundle (ASB) 是一个具有扩展名 .asb 的已签名 zip 文件。SDK 代码和资源在其中组织的方式类似于在 APK 中找到的方式。ASB 还包含几个配置文件,这些配置文件有助于生成可安装的 APK。

图 1. Android SDK Bundle 的内容。

以下列表更详细地描述了一些 ASB 文件

  • SdkBundleConfig.pb:一个以 proto 格式配置的文件,其中包含您的 SDK 依赖的运行时启用 SDK 列表。有关完整定义,请参阅 sdk_bundle_config.proto 文件。

  • modules.resm:一个 zip 文件,其中包含从 SDK 生成 APK 所需的所有数据。

  • SdkModulesConfig.pb:一个以 proto 格式配置的文件。此文件包含 SDK 的名称、版本和 SDK 入口点的类名,用于框架 (SandboxedSdkProvider)。有关完整定义,请参阅 sdk_modules_config.proto 文件。

  • base/:包含 SDK 代码和资源的单个模块。

    • manifest/:SDK 的清单,以 proto 格式。
    • dex/:以 DEX 格式编译的代码。可以提供多个 DEX 文件。
    • res/lib/assets/:这些目录与典型 APK 中的目录相同。在生成 SDK 的 APK 时,这些目录中的路径将保留。
    • root/:此目录存储稍后将重新定位到 SDK APK 根目录的文件。例如,它可能包含您的 SDK 使用 Class.getResource() 方法加载的基于 Java 的资源。此目录内的路径也将保留。
  • BUNDLE-METADATA:此目录包含包含对工具或应用商店有用的信息的元数据文件。此类元数据文件可能包括 ProGuard 映射和 SDK 的所有 DEX 文件的完整列表。此目录中的文件不会打包到 SDK 的 APK 中。

Android SDK 存档格式

Android SDK 存档是运行时启用 SDK 在 Maven 上的分发格式。它是一个具有文件扩展名 .asar 的 zip 文件。该文件包含应用程序构建工具生成依赖于您的运行时启用 SDK 的 Android App Bundle 所需的所有信息。

图 2. Android SDK 存档包的内容。

以下列表更详细地描述了一些 Android SDK 存档文件

  • SdkMetadata.pb:一个以 proto 格式配置的文件,其中包含 SDK 的名称、版本和用于签名为该 SDK 生成 APK 的密钥的证书摘要。有关完整定义,请参阅 sdk_metadata.proto 文件。

  • modules.resm:一个 zip 文件,其中包含从 SDK 生成 APK 所需的所有数据。这与 Android SDK Bundle 中的 .resm 文件相同。

  • AndroidManifest.xml:SDK 的清单文件,以文本 XML 格式。

其他资源

要详细了解如何使用 bundletool,请观看 App Bundles:使用 bundletool 和 Play Console 测试包