第3章 Gradle配置AAB构建:动态特性、bundle与feature依赖

Android App Bundle (AAB) 的构建核心在于 Gradle 的正确配置。本章系统讲解如何在 build.gradle 中声明动态功能模块、配置 bundle 约束、管理 feature 依赖关系,以及通过模块分割规则实现按需交付。掌握这些配置后,你将能构建出支持动态交付、条件分发和安装时分发的高质量 AAB 包。

3.1 开启 AAB 构建

在项目根 build.gradle 中引入 Android Gradle 插件(AGP 7.0+ 必须);在应用模块中设置 bundle 区块以启用 AAB 格式输出。

// 项目级 build.gradle
buildscript {
    dependencies {
        classpath "com.android.tools.build:gradle:7.4.2"
    }
}

// app/build.gradle
plugins {
    id 'com.android.application'
}

android {
    // 可选:明确指定 AAB 为唯一产出
    bundle {
        language {
            enableSplit = true
        }
        density {
            enableSplit = true
        }
        abi {
            enableSplit = true
        }
    }
}
从 AGP 7.0 开始,bundle {} 配置默认启用,无需手动开启;但显式设置 enableSplit 可以控制资源拆分行为。

3.2 动态功能模块声明

动态功能模块是一个独立的 Gradle 子模块,使用 com.android.dynamic-feature 插件。每个模块可以包含自己的代码、资源和清单,最终由 bundletool 打包为独立的 APK 切片。

// feature1/build.gradle
plugins {
    id 'com.android.dynamic-feature'
}

android {
    compileSdk 34

    defaultConfig {
        minSdk 21
        targetSdk 34
    }
}

dependencies {
    implementation project(':app')   // 必须依赖主应用
    implementation 'androidx.core:core:1.12.0'
}
动态功能模块必须依赖 project(':app'),否则构建时会报“dynamic feature must depend on application”错误。

3.3 bundle 配置与模块声明

在主应用 build.gradle 中,通过 dynamicFeatures 列表注册所有动态功能模块。同时可以在 bundle {} 内针对每个模块设置交付条件。

// app/build.gradle(完整配置)
android {
    dynamicFeatures = [':feature1', ':feature2']
}

// 在 feature 模块的 AndroidManifest 中定义交付条件
<dist:module dist:instant="false">
    <dist:delivery>
        <dist:install-time />          <!-- 安装时分发 -->
        <!-- <dist:on-demand />        <!-- 按需下载 -->
        <!-- <dist:conditional>...</dist:conditional> -->
    </dist:delivery>
    <dist:fusing dist:include="true" />
</dist:module>
动态功能模块的交付条件在 AndroidManifest.xml 中由 <dist:delivery> 标签指定,而非 Gradle 配置。这是常见误区。

3.4 dependencies 中的 feature 依赖

主应用模块无需在 dependencies 中显式添加 feature 依赖——dynamicFeatures 列表已经完成注册。但 feature 模块之间可以相互依赖,此时需要显式声明。

// feature2/build.gradle
dependencies {
    implementation project(':app')
    implementation project(':feature1')   // feature2 依赖 feature1
}
当 feature 之间存在依赖时,bundletool 会把所有被依赖的模块一起打包到同一个 APK 切片中,避免运行时报 ClassNotFoundException。

3.5 模块分割规则与资源拆分

AAB 的资源拆分依赖 bundle {} 配置和 AndroidManifest 中的 <dist:module> 属性。以下表格对比三种常见交付方式的关键区别:

交付方式XML 标签分发时机代码/资源打包方式
安装时 (install-time)<dist:install-time />APK 安装时一并下载合并到 base APK 或独立 APK
按需 (on-demand)<dist:on-demand />App 运行时通过 PlayCore API 下载独立 APK 切片,仅含该 feature
条件分发 (conditional)<dist:conditional>...</dist:conditional>满足条件(如国家、API 版本)时自动下载根据条件生成不同变体

资源拆分原则:各 feature 模块的资源必须位于模块内,不可引用主模块的 R ID;为避免 ID 冲突,建议使用 tools:replace="android:label" 等合并策略。

3.6 常见错误及解决方案

  • 错误:Dynamic feature module must depend on the base application module
    解决:确保 feature 的 build.gradle 中包含 implementation project(':app')。
  • 错误:Manifest merger failed : Attribute application@label value=(..) from AndroidManifest.xml
    解决:在两个模块的 <application> 标签上添加 tools:replace="android:label"。
  • 错误:Duplicate class found in modules (e.g. support-annotations)
    解决:使用 implementation 代替 api,或在主模块中 exclude group: '...'。
  • 错误:AAPT: error: style attribute '@android:attr/xxx' not found.
    解决:检查 compileSdk 是否一致,确保 feature 模块也引用相同的主题。
Gradle AAB 构建流程:模块依赖与打包 主应用 :app com.android.application 功能模块 :feature1 com.android.dynamic-feature 功能模块 :feature2 com.android.dynamic-feature bundle 配置 语言/密度/ABI 拆分 bundletool 构建 APK 切片 .aab 文件 动态功能模块清单中声明 <dist:delivery> 交付条件
上图展示了 Gradle 构建 AAB 时的核心依赖关系与打包流程。注意:主应用模块通过 dynamicFeatures 列表注册所有功能模块,各模块在 Manifest 中独立声明交付策略;最终的 bundletool 根据这些配置生成一个或多个 APK 切片。