第3章 动态功能模块清单配置

动态功能模块(Dynamic Feature Module)的清单配置是 SplitInstall 策略的核心起点。正确配置 AndroidManifest.xml 中的 dist:module 相关属性,决定了模块的安装时机、分发范围以及与基础模块的融合方式。本章将逐一剖析这些关键属性,并结合实际案例说明其作用机制。

3.1 dist:module 属性概述

dist:module 是 xmlns:dist 命名空间下用于描述模块分发策略的根元素。一个动态功能模块的清单文件中必须包含此元素,并至少指定 name 属性。完整属性集包括:

属性必填取值说明
dist:name是字符串模块的唯一标识,用于区分不同功能模块
dist:title否字符串资源引用模块的面向用户显示名称,用于安装对话框等场景
dist:onDemand是true / false控制模块是否为按需下载;false 表示安装时默认一起安装
dist:fusing是true / false是否允许模块合并到基础 APK 中(用于旧设备兼容)

3.2 安装时间控制:onDemand 与 install-time

dist:onDemand 属性直接决定模块的安装时机:

  • onDemand="false":模块在应用安装时即被下载并安装,用户无法手动卸载,体验上类似传统静态模块。
  • onDemand="true":模块不会在安装时下载,而是由应用运行时通过 SplitInstall API 按需触发下载。这是 SplitInstall 策略的主要应用场景。

下表对比两种模式的关键差异:

维度install-time(onDemand=false)on-demand(onDemand=true)
下载时机应用安装时运行时按需触发
用户感知安装包体积增大按需动态下载
卸载能力不可卸载可卸载(SplitInstall API 支持删除)
典型用途核心功能必须随安装存在高阶功能、低频功能、地区化内容
设计建议:将基础业务逻辑放在 install-time 模块中,将非核心功能(主题包、语言包、扩展工具)设置为 on-demand,最大限度地控制初始安装包体积。

3.3 fusing 配置:旧设备兼容性门控

dist:fusing 属性控制模块在设备不支持 Split APK 或动态安装时的行为:

  • fusing="true":当设备不支持动态功能模块时(例如 Android 4.x 系统),此模块会被合并入基础 APK,随主安装包一起分发。反之,支持动态功能的设备则按 onDemand 策略行为。
  • fusing="false":模块仅通过 Google Play 的动态分发机制传递;若设备不支持,该模块将不可用,应用需自行降级或提示。
注意事项:如果模块内包含不可延迟的关键资源(如支付相关类),请务必设置 fusing="true",避免旧设备上出现功能缺失;但 fusing 会增加基础 APK 的体积,需权衡利弊。

3.4 分割名称(split name)

每个动态功能模块编译后生成独立的 APK,其文件名和内部标识由 dist:name 决定。但部分模块需要与基础模块共享资源或依赖特定构建变体,此时可借助 dist:split 属性(或构建脚本中的 dist 配置)指定分割名称。通常默认分割名称等于 dist:name,但以下场景需显式指定:

  • 多个模块共享同一个资源类型(需同分割名下合并)。
  • 模块在不同构建变体(如 free/paid)中名称不同,但分割名需一致。
  • 自定义分割名称以匹配旧有分发机制。

3.5 完整配置示例

以下演示一个典型按需加载模块的 AndroidManifest.xml 配置:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    package="com.example.app.feature_ar">

    <dist:module
        dist:name="ar_mod"
        dist:title="@string/module_ar_title"
        dist:onDemand="true"
        dist:fusing="true" />

    <!-- 其他组件声明,例如 Activity、Service -->
    <application>
        <activity android:name=".ArActivity" />
    </application>
</manifest>

在该示例中,ar_mod 模块将在运行时按需下载,若设备不支持动态功能则合并入基础 APK。模块标题引用字符串资源,可在安装对话框展示。

3.6 常见错误与调试

  • 名称冲突:两个动态功能模块使用了相同的 dist:name,导致 Google Play 无法区分。务必保持模块名称唯一。
  • fusing 与 install-time 矛盾:设置 onDemand="false" 且 fusing="false" 会导致基础 APK 不包含模块,设备也无法动态安装,功能缺失。请确保至少一个为 true。
  • 缺失 dist:module 元素:模块清单中未声明 <dist:module>,编译时可能不产生分割 APK。
  • 混淆分割名称:构建脚本中 split 名称与清单中 dist:name 不一致,导致资源找不到。建议保持完全一致。
调试建议:使用 bundletool 检查生成的 AAB 文件中模块列表:
java -jar bundletool-all.jar get-device-spec --output=spec.json
再通过 java -jar bundletool-all.jar build-apks --bundle=app.aab --device-spec=spec.json 验证分割 APK 是否按预期生成。
动态功能模块清单配置关系图 AndroidManifest.xml dist:module name dist:onDemand dist:fusing 分割名称 (split) 安装时下载 (onDemand=false) 按需动态加载 (onDemand=true) 旧设备合并入基础APK (fusing=true) 分割名称影响 APK 文件命名 和资源合并策略

图中清晰展示了四个核心属性如何从清单文件出发,分别影响模块的安装时机、分发方式和设备兼容性。在实践中,应根据模块的功能定位和用户场景,合理组合这些配置。