Google Play 的应用服务模型使用 Android App Bundle 针对每位用户的设备配置生成并提供经过优化的 APK,因此用户只需下载其运行您的应用所需的代码和资源。
Play Feature Delivery 使用了 app bundle 的多种高级功能,让您可按条件分发或按需下载应用的某些功能。为此,您首先需要将这些功能从基础应用中分离出来,放入功能模块中。
功能模块构建配置
当您使用 Android Studio 创建新的功能模块时,IDE 会将以下 Gradle 插件应用于该模块的 build.gradle 文件。
// The following applies the dynamic-feature plugin to your feature module.
// The plugin includes the Gradle tasks and properties required to configure and build
// an app bundle that includes your feature module.
plugins {
id 'com.android.dynamic-feature'
}
适用于标准应用插件的很多属性也适用于功能模块。以下部分介绍了您应该和不应该在功能模块的 build 配置中包含的属性。
不应包含在功能模块 build 配置中的内容
由于每个功能模块都依赖于基本模块,因此它还会继承某些配置。因此,您应在功能模块的 build.gradle 文件中省略以下内容:
- 签名配置:使用您在基本模块中指定的签名配置对 app bundle 进行签名。
minifyEnabled属性:您只能从基础模块的 build 配置中为整个应用项目启用代码缩减。因此,您应该在功能模块中省略此属性。不过,您可以为每个功能模块指定其他 ProGuard 规则。versionCode和versionName:构建 app bundle 时,Gradle 会使用基本模块提供的应用版本信息。您应从功能模块的build.gradle文件中省略这些属性。
与基本模块建立关系
Android Studio 创建功能模块时,会通过将 android.dynamicFeatures 属性添加到基础模块的 build.gradle 文件中,使该功能模块对基础模块可见,如下所示:
// In the base module's build.gradle file.
android {
...
// Specifies feature modules that have a dependency on
// this base module.
dynamicFeatures = [":dynamic_feature", ":dynamic_feature2"]
}
此外,Android Studio 还会将基本模块添加为功能模块的依赖项,如下所示:
// In the feature module's build.gradle file:
...
dependencies {
...
// Declares a dependency on the base module, ':app'.
implementation project(':app')
}
指定其他 ProGuard 规则
虽然只有基础模块的 build 配置可以为应用项目启用代码缩减,但您可以使用 proguardFiles 属性为每个功能模块提供自定义 ProGuard 规则,如下所示。
android.buildTypes {
release {
// You must use the following property to specify additional ProGuard
// rules for feature modules.
proguardFiles 'proguard-rules-dynamic-features.pro'
}
}
请注意,这些 ProGuard 规则会在构建时与其他模块(包括基本模块)的规则合并在一起。因此,尽管每个功能模块都可以指定一组新规则,但这些规则会应用于应用项目中的所有模块。
部署应用
在开发支持功能模块的应用时,您可以像往常一样,从菜单栏中依次选择 Run > Run(或点击工具栏中的 Run 图标
),将该应用部署到连接的设备。
如果您的应用项目包含一个或多个功能模块,您可以通过修改现有的运行/调试配置以选择需要在部署应用时包含的功能,具体操作步骤如下:
- 从菜单栏中依次选择 Run > Edit Configurations。
- 在 Run/Debug Configurations 对话框的左侧面板中,选择所需的 Android App 配置。
- 在 General 标签页中的 Dynamic features to deploy 下,选中需要在部署应用时包含的每个功能模块旁边的复选框。
- 点击确定。
默认情况下,Android Studio 不会使用 App Bundle 部署您的应用,而是由 IDE 构建针对部署速度(而非 APK 大小)进行了优化的 APK,并将其安装到设备中。如需将 Android Studio 配置为通过 App Bundle 构建和部署 APK 以及免安装体验,请修改运行/调试配置。
使用功能模块实现自定义分发
功能模块的独特优势在于,能够自定义如何以及何时将应用的不同功能下载到搭载 Android 5.0(API 级别 21)或更高版本的设备上。例如,为了减小应用的初始下载大小,您可以将某些功能配置为按需下载,或者只能由支持特定功能(比如拍照或增强现实)的设备下载。
虽然您在以 app bundle 形式上传应用时默认会获得高度优化的下载内容,但更高级且可自定义的功能分发选项需要您使用功能模块对应用功能进行额外的配置和模块化。也就是说,功能模块提供了用于创建模块化功能的基块,而您可以将各项功能配置为按需下载。
假设我们有一款让用户可在网络购物平台上买卖商品的应用。您可以合理地将该应用的以下各项功能模块化处理为单独的功能模块:
- 账号登录与创建
- 浏览在线购物平台
- 上架商品
- 处理付款
下表列出了功能模块支持的不同分发选项,以及如何使用这些选项优化示例购物平台应用的初始下载大小。
| 分发选项 | 行为 | 示例用例 | 开始使用 |
|---|---|---|---|
| 安装时分发 | 默认情况下,未配置上述任何分发选项的功能模块会在应用安装时下载。这是一种重要的行为,因为这意味着,您可以逐步采用高级分发选项。例如,只有在使用 Play Feature Delivery 库完全实现按需下载机制之后,您才能受益于应用功能的模块化,并启用按需提供功能的选项。
此外,您的应用可以在之后请求卸载功能。因此,如果您在安装应用时需要某些功能,但在安装后不需要,则可以通过请求从设备中移除这些功能来缩减安装大小。 |
如果应用包含特定的指导 activity(比如关于如何在购物平台上买卖商品的交互式指南),可以配置为在应用安装时默认包含该功能。
不过,为了减小应用的安装大小,应用可以在用户完成培训后请求删除该功能。 |
使用未配置高级分发选项的功能模块对应用进行模块化处理。
如需了解如何通过移除用户可能不再需要的某些功能模块来减小应用的安装大小,请参阅管理已安装的模块。 |
| 按需分发 | 允许您的应用根据需要请求和下载功能模块。 | 如果在使用购物平台应用的用户中,只有 20% 的人发布待售商品,有一个不错的策略可以减小大多数用户的初始下载大小,那就是将拍照、输入商品描述及上架商品的功能配置为按需下载。也就是说,您可以将应用销售功能的功能模块配置为仅在用户表示有兴趣在市场上出售商品时才下载。
此外,如果用户在一段时间后不再销售商品,应用可以通过请求卸载该功能来缩减其安装大小。 |
创建功能模块并配置按需分发。然后,应用就可以使用 Play Feature Delivery 库请求按需下载该模块。 |
| 根据条件分发 | 让您可以指定某些用户设备要求(例如硬件功能、语言区域和最低 API 级别),以确定是否在应用安装时下载模块化功能。 | 如果购物平台应用的用户遍布全球,您可能需要支持仅在特定地区或区域常用的付款方式。为了减小应用的初始下载大小,您可以创建单独的功能模块处理特定类型的支付方式,并将这些模块根据用户的注册区域视条件安装在用户设备上。 | 创建功能模块并配置按条件分发。 |
| 免安装分发 | Google Play 免安装体验让用户无需在设备上安装应用即可与应用互动。用户可以通过 Google Play 商店中的“立即体验”按钮或您创建的网址体验您的应用。这种内容提供形式可让您更轻松地提高应用的使用率。
借助免安装分发,您可以使用 Google Play 免安装体验让用户无需安装即可立即体验应用的某些功能。 |
假设有一款游戏,游戏的前几个关卡包含在一个轻量级功能模块中。您可以为该模块支持免安装体验,以便用户通过网址链接或“立即试用”按钮即时体验游戏,而无需安装应用。 | 创建功能模块并配置免安装分发。然后,应用就可以使用 Play Feature Delivery 库请求按需下载该模块。
请注意,使用功能模块对应用功能进行模块化处理只是第一步。为了支持 Google Play 免安装体验,应用的基本模块和支持免安装体验的给定功能的下载大小必须满足严格的大小限制。如需了解详情,请参阅通过缩减应用或游戏大小来启用免安装体验。 |
为资源构建 URI
如果您要使用 URI 访问存储在功能模块中的资源,请参考以下内容,了解如何使用 Uri.Builder() 生成功能模块资源 URI:
Kotlin
val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
.appendPath(resources.getResourceTypeName(resId))
.appendPath(String.format("%s:%s",
resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
resources.getResourceEntryName(resId)
))
.build()
Java
String uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
.appendPath(resources.getResourceTypeName(resId))
.appendPath(String.format("%s:%s",
resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
resources.getResourceEntryName(resId)
))
.build().toString();
系统会在运行时构造资源路径的每一部分,从而确保在加载拆分 APK 后生成正确的命名空间。
下面举例说明如何生成 URI,假设您有一个应用和功能模块,它们的名称如下:
- 应用软件包名称:
com.example.my_app_package - 功能的资源软件包名称:
com.example.my_app_package.my_dynamic_feature
如果上述代码段中的 resId 引用的是功能模块中名为“my_video”的原始文件资源,则上述 Uri.Builder() 代码将输出以下内容:
android.resource://com.example.my_app_package/raw/com.example.my_app_package.my_dynamic_feature:my_video
然后,您的应用可以使用此 URI 访问功能模块的资源。
如需验证 URI 中的路径,您可以使用 APK 分析器检查功能模块 APK 并确定软件包名称:
有关功能模块的注意事项
借助功能模块,您可以提高构建速度和工程速度,并广泛自定义应用功能的交付,以缩减应用的大小。不过,在使用功能模块时,请注意以下一些限制和极端情况:
- 如果使用条件分发或按需分发在单个设备上安装 50 个或更多功能模块,可能会导致性能问题。安装时模块(未配置为可移除)会自动包含在基础模块中,并且在每台设备上仅算作一个功能模块。
- 将您为安装时分发配置的可移除模块数量限制为不超过 10 个。否则,应用的下载和安装时间可能会增加。
- 只有搭载 Android 5.0(API 级别 21)及更高版本的设备才支持按需下载和安装功能。如需使功能适用于更低版本的 Android,请在创建功能模块时启用融合功能。
- 启用
SplitCompat,以便您的应用可以访问按需分发的已下载功能模块。 - 在将
android:exported设置为true的情况下,功能模块不得在其清单中指定 activity。这是因为,当其他应用尝试启动该 activity 时,无法保证设备已下载该功能模块。此外,应用在尝试访问功能的代码和资源之前,应该先确认该功能已下载。如需了解详情,请参阅管理已安装的模块。 - 由于 Play Feature Delivery 要求您使用 app bundle 发布应用,因此请务必了解 app bundle 的已知问题。
功能模块清单参考文档
当使用 Android Studio 创建新功能模块时,该 IDE 会纳入模块作为功能模块正常运行所需的大多数清单属性。此外,某些属性由构建系统在编译时注入,因此您无需自行指定或修改它们。 下表列出了对功能模块非常重要的清单属性。
| 属性 | 说明 |
|---|---|
| <manifest | 这是一个典型的 <manifest> 块。 |
| xmlns:dist="http://schemas.android.com/apk/distribution" | 指定新的 dist: XML 命名空间,下文将对此进行进一步说明。 |
| split="split_name" |
当 Android Studio 构建 app bundle 时,会包含该属性。因此,您不应自行添加或修改此属性。
定义模块的名称,当应用使用 Play Feature Delivery 库发出按需模块请求时会指定该名称。 Gradle 如何确定该属性的值: 默认情况下,当您使用 Android Studio 创建功能模块时,IDE 会使用您指定的模块名称,在 Gradle 设置文件中将该模块标识为 Gradle 子项目。
当您构建 app bundle 时,Gradle 会使用子项目路径的最后一个元素在模块清单中注入此清单属性。例如,如果您在 |
| android:isFeatureSplit="true | false"> |
当 Android Studio 构建 app bundle 时,会包含该属性。因此,您不应手动添加或修改此属性。
指定此模块为功能模块。基本模块和配置 APK 中的清单要么省略此属性,要么将其设置为 |
| <dist:module | 定义用于确定如何将模块打包和分发为 APK 的属性。 |
| dist:instant="true | false" |
指定是否应通过 Google Play 免安装体验为模块启用免安装体验。
如果应用包含一个或多个启用免安装体验的功能模块,您也必须为基本模块启用免安装体验。如果您使用的是 Android Studio 3.5 或更高版本,当您创建支持免安装体验的功能模块时,IDE 会为您完成此操作。 在设置 |
| dist:title="@string/feature_name"> |
为模块指定一个面向用户的名称。例如,当设备请求确认下载时,便可能会显示该名称。
您需要在基础模块的 |
| <dist:fusing dist:include="true | false" /> |
指定是否在面向搭载 Android 4.4(API 级别 20)及更低版本的设备的 multi-APK 中包含此模块。
此外,当您使用 |
| <dist:delivery> | 封装自定义模块分发的选项,如下所示。请注意,每个功能模块必须只配置这些自定义分发选项的一种类型。 |
| <dist:install-time> |
指定模块应在安装时可用。对于未指定其他类型的自定义分发选项的功能模块,这是默认行为。
如需详细了解安装时下载,请参阅配置安装时分发。 此节点还可以指定条件,用于限定要下载模块的设备所需满足的某些要求,例如设备功能,用户所在国家/地区或最低 API 级别。如需了解详情,请参阅配置按条件分发。 |
| <dist:removable dist:value="true | false" /> |
当未设置或设置为 当 默认为 注意:只有在使用 Android Gradle 插件 4.2 或从命令行使用 bundletool v1.0 时,才能使用此功能。 |
| </dist:install-time> | |
| <dist:on-demand /> |
指定应以按需下载的形式分发模块。也就是说,模块在安装时不会下载,但应用可以稍后请求下载。
如需详细了解按需下载,请参阅配置按需分发。 |
| </dist:delivery> | |
| </dist:module> | |
| <application
android:hasCode="true | false"> … </application> |
如果功能模块没有生成 DEX 文件(也就是说,它不包含之后编译成 DEX 文件格式的代码),您必须执行以下操作(否则,您可能会遇到运行时错误):
|
| … </manifest> |
其他资源
如需详细了解如何使用功能模块,请使用以下资源。
博文
- 能够助力您在 Google Play 上开发和发布产品并拓展业务的新功能
- 最新的 Android App Bundle 更新,包括针对其他语言的 API
- Patchwork Plaid - 模块化故事
视频
服务条款和数据安全
访问或使用 Play Feature Delivery 库即表示您同意接受 Play Core 软件开发套件服务条款。请先阅读并了解所有适用的条款及政策,然后再访问该库。
数据安全
Play Core 库是您的应用与 Google Play 商店的运行时接口。因此,当您在应用中使用 Play Core 时,Play 商店会运行自己的进程,其中包括根据 Google Play 服务条款处理数据。以下信息说明了 Play Core 库如何通过处理数据来处理从您的应用发来的特定请求。
其他语言 API
| 收集的使用情况数据 | 已安装语言的列表 |
| 数据收集目的 | 我们会利用收集的数据来提供不同语言版本的应用,以及在应用更新后保留已安装的语言。 |
| 数据加密 | 数据会加密。 |
| 数据共享 | 我们不会将收集的数据发送给任何第三方。 |
| 数据删除 | 我们会在固定保留期限过后删除收集的数据。 |
Play Feature Delivery
| 收集的使用情况数据 |
设备元数据 应用版本 |
| 数据收集目的 | 我们会利用收集的数据来向设备提供合适的模块,以及在更新后及备份和恢复后保留已安装的模块。 |
| 数据加密 | 数据会加密。 |
| 数据共享 | 我们不会将收集的数据发送给任何第三方。 |
| 数据删除 | 我们会在固定保留期限过后删除收集的数据。 |
虽然我们力求做到尽可能公开透明,但对于 Google Play 的“数据安全”部分要求您填写的关于应用的用户数据收集、分享和安全做法的表单,您需自行负责决定如何回应。