工程结构搭建

动态功能模块(Dynamic Feature Module)是 Android App Bundle 的核心扩展机制,允许将功能拆分为独立的模块,按需下发。本章将指导你完成从零搭建支持 SplitInstall 的 Android 项目,包括项目创建、模块添加、Gradle 依赖配置以及应用 ID 的设置。

2.1 动态功能模块的项目架构

一个支持按需加载的 Android 项目包含两类模块:Base 模块(即 app 模块)和若干 动态功能模块。Base 模块包含应用的公共代码、资源及 AndroidManifest;动态功能模块则封装独立的功能,通过 SplitInstallManager 按需下发。

对比项常规模块动态功能模块
打包方式始终打包到 APK单独打包为 split APK
安装时机安装应用时一并安装按需通过 Play Core API 下载安装
模块类型Android Library / Applicationcom.android.dynamic-feature
dist 配置不需要必须配置 distributions
应用ID单一 applicationId基础 ID + 模块后缀
动态功能模块项目结构图 Base APK app 模块 动态功能模块 A :feature_a 动态功能模块 B :feature_b Play Core API SplitInstall 下载 Gradle 依赖关系 Base 模块通过 implementation project(‘:feature_xx’) 依赖动态功能模块;动态模块依赖 Base 模块

2.2 创建 Android 项目并添加动态功能模块

前提:Android Studio 4.0+,Gradle 6.8+。推荐使用 Android App Bundle 发布格式。

创建步骤:

  1. 通过 File > New > New Project 创建标准 Android 项目,选择“Empty Activity”。
  2. 项目创建后,右键点击项目根目录,选择 New > New Module > Dynamic Feature Module。
  3. 在向导中设置模块名称(如 feature_on_demand),指定 Base 模块 applicationId(通常为 com.example.app),并勾选“Install-time inclusion”为 Never(按需安装)。
  4. 完成向导后,AS 会自动生成模块目录及 build.gradle,并修改根 settings.gradle。

生成的动态功能模块 build.gradle 基础配置示例:

apply plugin: 'com.android.dynamic-feature'

android {
    compileSdk 34

    defaultConfig {
        // 不要在此设置 applicationId,由 Base 模块提供
        minSdk 21
        targetSdk 34
    }
}

dependencies {
    implementation project(':app')  // 依赖 Base 模块
}

2.3 配置 Gradle 模块依赖

动态功能模块必须通过 Base 模块分发,因此需要在 Base 的 build.gradle 中声明对动态模块的 implementation 依赖:

// app/build.gradle
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation project(':feature_on_demand')
    // 其他依赖...
}
注意:只能使用 implementation,不能使用 api 或 compileOnly。同时确保 settings.gradle 已包含模块引用:
// settings.gradle
include ':app'
include ':feature_on_demand'

在动态功能模块的 build.gradle 中必须配置 distributions,指定模块名称用于 Play Core API 识别:

// feature_on_demand/build.gradle
android {
    dynamicFeatures = [':feature_on_demand']  // 模块标识
}

2.4 设置基础与应用 ID

Base 模块的 applicationId 即应用唯一标识,动态功能模块不需要设置独立的 applicationId,而是使用 distinctApplicationId 或由 Play 自动生成。推荐做法:

  • Base 模块:android.defaultConfig.applicationId = "com.example.app"
  • 动态功能模块:在 build.gradle 中通过 distinctApplicationId 指定模块后缀,但通常交由系统自动处理,仅需保证 applicationIdSuffix 不被使用。

若需要为动态模块指定不同的基本 ID(例如为了测试),可在 defaultConfig 中配置:

// 动态模块 build.gradle
android {
    defaultConfig {
        // 显式设置 distinctApplicationId(可选)
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            // 模块后缀会在基础 ID 上追加
        }
    }
}

常见的 ID 配置对照:

模块applicationId说明
app(Base)com.example.app主 ID
feature_on_demand自动继承 com.example.app 并追加后缀Play 管理 split ID
feature_ads(同上)可直接使用 distinctApplicationId

2.5 常见错误与调试

错误 1:Module not found
运行 SplitInstallManager.startInstall() 时抛出 SplitInstallException,提示模块未找到。
原因:distributions 配置缺失或模块名不匹配。
解决:检查动态模块 build.gradle 中 dynamicFeatures 的值,确保与 API 调用时传入的模块名一致。
错误 2:Gradle 同步失败 - “Unable to find module with Gradle path”
原因:settings.gradle 未包含动态模块。
解决:在 settings.gradle 中添加 include ':feature_name'。
调试建议:使用 adb shell pm list features 查看当前已安装的 split APK。运行 App Bundle 构建时,可在 build/outputs/bundle/release/ 下检查生成的 .aab 文件,确认动态模块的 module.zip 已包含在内部。

完成上述配置后,项目结构已满足动态功能模块的构建和分发要求。下一步将实现 SplitInstall 的下载与安装逻辑。