Dynamic Delivery 简介

Google Play 的应用服务模型名为“Dynamic Delivery”,该模型使用 Android App Bundle 针对每位用户的设备配置生成并提供经过优化的 APK,因此用户只需下载其运行您的应用所需的代码和资源。您无需再编译、签署和管理多个 APK 以支持不同的设备,而用户也可以获得更小、更优化的下载文件包。

对于大多数应用项目而言,适当地编译 app bundle 以支持使用 Dynamic Delivery 提供经过优化的拆分 APK 并不费力。例如,如果您已经按照既定惯例组织管理应用的代码和资源,则只需要使用 Android Studio 或使用命令行编译已签名的 Android App Bundle,然后将它们上传到 Google Play 即可。然后,您就可以自动获享 Dynamic Delivery 的优势了。

要支持 Dynamic Delivery 的高级功能(例如将应用的某些功能配置为根据特定条件提供或按需下载),请阅读介绍如何自定义功能提供方式的部分。

Dynamic Delivery 与拆分 APK

Dynamic Delivery 的一个基本组件就是 Android 5.0(API 级别 21)及更高版本上提供的拆分 APK 机制。拆分 APK 与常规 APK 非常相似,其中包含经过编译的 DEX 字节码、资源和 Android 清单。但是,Android 平台能够将已安装的多个拆分 APK 视为一个应用。也就是说,您可以安装多个共用代码和资源的拆分 APK,使它们在设备上看起来同属于安装的一个应用。

拆分 APK 的好处是能够将单体式 APK 拆分为更小的独立软件包,这些软件包可“按需”安装在用户设备上。单体式 APK 是指一个 APK 中包含应用所支持的全部功能和设备配置对应的代码和资源。

例如,一个拆分 APK 可能包含只有少数用户需要的附加功能所对应的代码和资源,而另一个拆分 APK 则只包含特定语言或屏幕密度所对应的资源。这些拆分 APK 可以根据用户的请求或设备的需要单独下载和安装。

下面介绍可以一起安装在设备上以形成完整应用体验的不同类型的 APK。本页的后面几节将介绍如何配置应用项目以支持这些 APK。

  • 基本 APK:此 APK 中包含了所有其他拆分 APK 都可以访问的代码和资源,并提供应用的基本功能。当用户请求下载您的应用时,会首先下载并安装该 APK。这是因为只有基本 APK 的清单才包含关于应用的服务、内容提供方、权限、平台版本要求和对系统功能的依赖性的完整声明。Google Play 会根据项目的应用模块(即基本模块)为应用生成基本 APK。如果您想减小应用的初始下载大小,请一定要注意,此模块中包含的所有代码和资源都包含在应用的基本 APK 中。
  • 配置 APK:每个配置 APK 都包含针对特定屏幕密度、CPU 架构或语言的原生库和资源。当用户下载您的应用时,他们的设备只会下载并安装该设备对应的配置 APK。每个配置 APK 都是基本 APK 或动态功能 APK 的依赖项。也就是说,配置 APK 会随它们为之提供代码和资源的 APK 一起下载和安装。与基本模块和动态功能模块不同,您不需要为配置 APK 单独创建模块。如果您在为基本模块和动态功能模块组织管理配置专用的备用资源时遵循了标准实践,Google Play 会自动为您生成配置 APK
  • 动态功能 APK:每个动态功能 APK 都包含您应用中的某项功能的代码和资源,并且您使用动态功能模块对相应功能进行了模块化处理。然后,您可以通过 Dynamic Delivery 自定义如何及何时将该功能下载到设备上。例如,使用 Google Play 核心库,可在将基本 APK 安装到设备上之后再按需安装动态 APK,以向用户提供额外的功能。假设我们有一款聊天应用,它仅在用户想要拍摄并发送照片时才下载并安装该功能。由于动态功能在一开始安装应用时可能不可用,因此应该将所有公用代码和资源包含在基本 APK 中。也就是说,您的动态功能应假定在安装时只有基本 APK 的代码和资源可用。Google Play 会根据项目的动态功能模块为应用生成动态功能 APK。

假设我们有一款包含三个动态功能模块并支持多种设备配置的应用。下图 1 展示了该应用的各个 APK 的依赖关系树。请注意,基本 APK 构成树的头部,所有其他 APK 都依赖于基本 APK。(如果您想知道这些 APK 的模块在 Android App Bundle 中的表示方式,请参阅 Android App Bundle 格式。)

基本 APK 位于树的头部,并有动态功能 APK 依赖于基本 APK。配置 APK 构成依赖关系树的叶节点,此类 APK 中包含了基本 APK 和各动态功能 APK 的设备配置专用代码和资源。

图 1. 使用拆分 APK 构建的应用的依赖关系树

请注意,您无需自己编译这些 APK,Google Play 会根据您通过 Android Studio 编译的单个签名 app bundle 来为您完成编译。如需详细了解 app bundle 的格式和编译方式,请转到编译、部署和上传 Android App Bundle

搭载 Android 4.4(API 级别 19)及更低版本的设备

由于搭载 Android 4.4(API 级别 19)及更低版本的设备不支持下载和安装拆分 APK,因此 Google Play 会为这些设备提供名为 multi-APK 的单个 APK,该 APK 针对相应设备配置进行了优化。也就是说,multi-APK 提供完整的应用体验,但不包含不必要的代码和资源,例如用于其他屏幕密度和 CPU 架构的代码和资源。

但是,它们包含了应用所支持的所有语言的资源。例如,这让用户无需另外下载 multi-APK 即可更改应用的首选语言设置。

multi-APK 无法在以后按需下载动态功能模块。要在此 APK 中包含动态模块,在创建动态功能模块时,必须停用 On-demand 或启用 Fusing

请记住,Dynamic Delivery 让您不必为应用支持的每种设备配置分别编译、签署、上传和管理 APK。您仍然只需为整个应用编译和上传一个 app bundle,剩下的工作可交由 Google Play 处理。因此,无论您是否打算支持搭载 Android 4.4 或更低版本的设备,Dynamic Delivery 都为您和您的用户提供了灵活的服务机制。

应用模块化

对应用进行模块化处理就是将应用项目的逻辑组件拆分成独立模块的过程。

要将应用功能妥善地重新组织到不同的独立组件中,您需要花时间仔细构思。不过,模块化能给您的项目带来以下好处:

  • 并行开发:通过将应用的逻辑组件拆分成不同的模块,组织中的不同团队或个人可以认领并负责不同的模块,从而减少合并冲突以及对其他团队的干扰。此外,如果有应用的各个部分共用的逻辑,则可以使用库模块来促进代码重用和封装。
  • 缩短编译时间:编译系统(例如使用 Gradle 的 Android Studio 编译系统)针对划分成模块的项目进行了优化。例如,如果您在配备多核处理器的工作站上启用 Gradle 的并行项目执行优化,编译系统就能够并行地编译多个模块,从而显著减少编译时间。项目的模块化程度越高,编译性能的改进就越明显。
  • 自定义功能提供方式:将应用的功能模块化处理为动态功能模块,是利用 Dynamic Delivery 的自定义提供选项(如按需提供、根据条件提供和免安装提供)的前提条件。构建按需动态功能所需的工作量更大,并可能需要重构应用。因此,请仔细考虑“模块化处理为动态功能”和“自定义提供选项”这两者对应用的哪些功能益处最大。

要按应用功能恰当地对项目进行模块化处理,您需要花费一定的时间认真考虑。当您决定开始对应用进行模块化处理时,首先应该用支持模块化功能所需的必要属性来配置基本模块。然后,您可以将动态功能模块配置为在安装时提供,在不改变应用当前行为的前提下,逐步对应用功能进行模块化处理。

利用动态功能模块,实现自定义提供方式

Dynamic Delivery 的独特优势在于能够自定义应用的不同功能如何以及何时下载到搭载 Android 5.0(API 级别 21)或更高版本的设备上。例如,为了减小应用的初始下载大小,您可以将某些功能配置为按需下载,或者只能由支持特定功能(比如拍照或增强现实)的设备下载。

虽然将应用作为 app bundle 上传时,默认即可获得高度优化的下载文件,但如需使用更高级和可自定义的功能提供选项,您就必须使用“动态功能模块”对应用的功能进行额外的配置和模块化处理。也就是说,动态功能模块为您提供了用于构建模块化功能的基块,而您可以将这些功能配置为按需下载。

假设我们有一款让用户可在网络购物平台上买卖商品的应用。您可以合理地将应用的以下功能模块化处理为独立的动态功能模块:

  • 帐户登录与创建
  • 浏览在线购物平台
  • 上架商品
  • 处理付款

下表列出了动态功能模块支持的不同提供选项,以及如何使用这些选项来优化示例购物平台应用的初始下载大小。

提供选项 行为 示例用例 使用入门
在安装时提供 默认情况下,未配置上述任何提供选项的动态功能模块会在安装应用时下载。这是一种重要的行为方式,有助于您逐步采用更高级的功能提供选项。例如,只有在使用 Google Play 核心库完全实现按需下载机制之后,您才能受益于应用功能的模块化,并启用按需提供功能的选项。

此外,您的应用可以在之后请求卸载功能。因此,如果您在安装应用时需要某些功能,但之后又不需要了,您可以请求从设备上移除相关功能来减小安装大小。

如果应用包含特定的指导 Activity(比如关于如何在购物平台上买卖商品的交互式指南),可以配置为在应用安装时默认包含该功能。

但是,为了减小应用的安装大小,应用可在用户完成该指导后请求删除该功能。

使用未配置高级提供选项的动态功能模块对应用进行模块化处理

要了解如何通过移除用户可能不再需要的动态功能模块来减小应用的安装大小,请参阅管理已安装的模块

按需提供 允许您的应用按需请求和下载动态功能模块。 如果在使用购物平台应用的用户中,只有 20% 的人发布待售商品,有一个不错的策略可以减少大多数用户的初始下载大小,那就是将拍照、输入商品描述及上架商品的功能配置为按需下载。也就是说,您可以为应用的销售功能配置动态功能模块,使该功能仅在用户希望在购物平台上销售商品时才下载。

此外,如果用户在一段时间后不再出售商品,应用可以通过请求卸载该功能来减小其安装大小。

创建动态功能模块并配置按需提供功能。然后,应用就可以使用 Google Play 核心库请求按需下载该模块。
根据条件提供 允许您指定特定的用户设备需求(例如硬件特性、区域设置和最低 API 级别),以确定是否在安装应用时下载模块化功能。 如果购物平台应用的用户遍布全球,您可能需要支持仅在特定地区使用的支付方式。为了减小应用的初始下载大小,您可以创建单独的动态功能模块来处理特定类型的支付方式,并将这些模块配置为根据用户的注册区域视条件安装在用户设备上。 创建动态功能模块并配置根据条件提供功能
免安装提供 Google Play 免安装体验让用户无需在设备上安装 APK 即可与应用互动。用户可以通过 Google Play 商店中的“立即体验”按钮或您创建的网址体验您的应用。这种内容提供形式可让您更轻松地提高应用的使用率。

借助免安装提供方式,您可利用 Google Play 免安装体验,让用户无需安装就能够立即体验应用的特定功能。

假设有一款游戏,游戏的前几个关卡包含在轻量级动态功能模块中。您可以启用该模块的免安装体验,这样用户就可以通过网址或“立即体验”按钮来体验游戏,而无需安装应用。 创建动态功能模块并配置免安装提供功能。然后,应用就可以使用 Google Play 核心库请求按需下载该模块。

请注意,使用动态功能模块来模块化处理应用功能只是第一步。要支持 Google Play 免安装体验,应用基本模块的下载大小和给定的启用免安装体验的动态功能必须满足严格的大小限制。要了解详情,请参阅通过减少应用或游戏大小来启用免安装体验

动态功能模块清单

当使用 Android Studio 创建新的动态功能模块时,IDE 中包含了要使该模块像动态功能一样运作所需要的大部分清单属性。此外,有些属性是在编译时由编译系统注入的,因此您不需要自己指定或修改它们。下表列出了对动态功能模块非常重要的清单属性。

属性 说明
<manifest
...
这是典型的 <manifest> 代码块。
xmlns:dist="http://schemas.android.com/apk/distribution" 指定一个新的 dist: XML命名空间,如下所述。
split="split_name" 当 Android Studio 编译 app bundle 时,会包含该属性。因此,您不应自行添加或修改此属性

定义模块的名称,当应用使用 Google Play 核心库发出按需模块请求时会指定该名称。

Gradle 如何确定该属性的值:

默认情况下,当您使用 Android Studio 创建动态功能模块时,IDE 会使用您指定的模块名称,在 Gradle 设置文件中将该模块标识为 Gradle 子项目。

在您编译 app bundle 时,Gradle 会使用子项目路径的最后一个元素将此清单属性注入到模块的清单中。例如,如果您在 MyAppProject/features/ 目录中创建新的动态功能模块,并指定“dynamic_feature1”作为其模块名称,IDE 则会在 settings.gradle 文件中添加 ':features:dynamic_feature1' 作为子项目。编译 app bundle 时,Gradle 会在模块清单中注入 <manifest split="dynamic_feature1">

android:isFeatureSplit="true | false"> 当 Android Studio 编译 app bundle 时,会包含该属性。因此,您不应自行添加或修改此属性

指定此模块为动态功能模块。基本模块和配置 APK 中的清单要么省略该属性,要么将其设置为 false

<dist:module 这一新的 XML 元素定义了一些属性,这些属性可确定如何打包模块并作为 APK 分发。
dist:instant="true | false" 指定是否应通过 Google Play 免安装体验为模块启用免安装体验。

如果应用包含一个或多个启用免安装体验的动态功能模块,则也必须为基本模块启用免安装体验。如果您使用的是 Android Studio 3.3 或更高版本,则当您创建启用免安装体验的动态功能模块时,IDE 会为您完成此操作。

在设置 dist:onDemand="true" 时,不能将此 XML 元素设置为 true。不过,根据免安装体验的运作方式,您仍可使用 Google Play 核心库按需下载启用免安装体验的动态功能。当用户下载并安装您的应用时,设备会默认下载并安装启用免安装体验的动态功能以及基本 APK。

dist:onDemand="true | false" 指定模块是否应支持按需下载。也就是说,如果将该属性设置为 true,则模块在安装时不会下载,但应用可以稍后请求下载。

如果将该属性设置为 false,则在用户首次下载并安装您的应用时,就会包含该模块。

要详细了解按需下载,请参阅使用 Google Play 核心库下载模块

dist:title="@string/feature_name" 为模块指定一个面向用户的名称。例如,当设备请求确认下载时,便可能会显示该名称。

您需要将此名称的字符串资源包含在基本模块的 module_root/src/source_set/res/values/strings.xml 文件中。

<dist:fusing dist:include="true | false" />
</dist:module>
指定是否在面向搭载 Android 4.4(API 级别 20)及更低版本的设备的 multi-APK 中包含此模块。

此外,当您使用 bundletool 从 app bundle 生成 APK 时,只有将此属性设置为 true 的动态功能模块才会包含在通用 APK 中。通用 APK 是一个单体式 APK,其中包含了应用所支持的所有设备配置的代码和资源。

<application
android:hasCode="true | false">
...
</application>
如果动态功能模块没有生成 DEX 文件(也就是说,它不包含之后编译成 DEX 文件格式的代码),则您必须执行以下操作(否则,您可能会遇到运行时错误):
  1. 在动态功能模块的清单中将 android:hasCode 设置为 "false"
  2. 将以下内容添加到基本模块的清单中:
    
        <application
          android:hasCode="true"
          tools:replace="android:hasCode">
          ...
        </application>
        

测试 Dynamic Delivery

测试 Dynamic Delivery 的最佳方法是通过 Google Play 商店进行测试。这是因为 Dynamic Delivery 的许多优势的实现都有赖于通过 Google Play 商店生成、签署和提供优化的 APK。因此,无论您是通过上传 app bundle 来实现对 Dynamic Delivery 的基本支持,还是配置自定义提供选项,都应该使用以下方法通过 Dynamic Delivery 测试应用:

  • 通过网址分享应用。通过这种方式,您能够以最快的速度上传 app bundle 并通过 Google Play 商店中的链接将应用分享给信任的测试人员。此外,这也是测试自定义提供选项(如按需下载功能)的最快方法。
  • 设置开放式、封闭式或内部测试。该方法提供结构化的测试通道,可以在面向外部用户发布应用之前,充分地测试应用的最终发布版本。

有关动态功能的注意事项

如果您想发布包含动态功能模块的正式版应用,请记住以下几点:

  • 只有搭载 Android 5.0(API 级别 21)及更高版本的设备才支持按需下载和安装动态功能。要使动态功能适用于更低版本的 Android,请确保在创建动态功能模块时启用 Fusing
  • 请确保启用 SplitCompat,这样应用才能立即访问下载的动态功能模块。
  • 如果动态功能的下载大小较大,应用将需要获得用户确认才能将动态功能模块下载到设备上。
  • 动态功能模块不得在其清单中指定 Activity 并将 android:exported 设置为 true。这是因为,当其他应用尝试启动相应 Activity 时,无法保证设备已下载相应的动态功能模块。此外,应用在尝试访问动态功能的代码和资源之前,应该先确认该功能已下载。要了解详情,请参阅管理已安装的模块
  • 由于 Dynamic Delivery 要求使用 app bundle 发布应用,因此请确保您了解 app bundle 的已知问题

其他资源

要详细了解如何使用/支持 Dynamic Delivery,请使用以下资源。

示例

Codelab

  • 您的第一个 Android App Bundle:一个探索 Android App Bundle 基本原理的 codelab,向您展示了如何使用 Android Studio 快速开始编译您自己的 App Bundle。此 codelab 还探索了如何使用 bundletool 测试 app bundle。
  • 按需模块:可帮助您创建按需下载和安装动态功能的应用。

博文

视频