2. 多模块项目Gradle配置结构:root build.gradle、settings.gradle、模块级build.gradle职责、统一配置方式
多模块项目(Multi‑Module)的 Gradle 配置由三个层级的文件共同完成:settings.gradle 声明模块边界,root build.gradle 控制全局行为,模块级 build.gradle 描述各自构建逻辑。理解每个文件的职责边界,并掌握统一配置方法,是避免版本冲突、缓存同步失败的基础。
2.1 配置文件职责对比
| 文件 | 执行阶段 | 核心职责 | 常见误区 |
|---|---|---|---|
settings.gradle | Initialization | 声明模块列表、配置仓库、版本目录 | 在 settings.gradle 中编写依赖声明 |
root build.gradle | Configuration | 全局插件版本、仓库、allprojects/subprojects 公共配置 | 将模块特有逻辑放在 root 中 |
模块 build.gradle | Configuration | 模块专属依赖、插件应用、自定义任务 | 重复书写同一组配置(如 Kotlin 版本) |
2.2 settings.gradle:模块声明与仓库管理
在 Initialization 阶段解析,它决定了哪些模块被纳入构建树。典型结构:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "MyApp"
include(":app")
include(":library:core")
include(":library:network")
注意:pluginManagement 和 dependencyResolutionManagement 只能出现在 settings.gradle 中,不能在其他文件重复定义。使用 include() 而非扁平字符串,避免路径混淆。
2.3 root build.gradle:全局配置与插件声明
根模块(:)的 build.gradle 通常只放置项目级插件版本声明和公共配置:
plugins {
id("com.android.application") version "8.2.0" apply false
id("com.android.library") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
}
allprojects {
// 所有项目(包括根模块)共享的配置
configurations.configureEach {
resolutionStrategy.cacheChangingModulesFor(0, "seconds")
}
}
subprojects {
// 只影响子模块
apply(plugin = "org.jetbrains.kotlin.android")
android {
compileSdk = 34
defaultConfig.minSdk = 21
}
}
警告:allprojects 和 subprojects 块会泄漏到所有子模块,容易造成构建缓存冲突。AGP 8.0+ 推荐使用 Convention Plugins 替代。
2.4 模块级 build.gradle:模块专属配置
每个子模块的 build.gradle 聚焦于自身职责:
plugins {
id("com.android.library")
id("kotlin-android")
id("maven-publish")
}
android {
namespace = "com.example.library.core"
compileSdk = 34
defaultConfig {
minSdk = 21
}
}
dependencies {
implementation(project(":library:network"))
implementation("com.google.android.material:material:1.11.0")
}
遵循单一职责:模块级只配置本模块所必需的参数,例如不同的 minSdk、buildTypes 或特有的依赖。全局统一参数通过 Convention Plugins 注入。
2.5 统一配置方式
为了避免在每个模块的 build.gradle 中重复写相同的 compileSdk、minSdk、targetSdk 等参数,Gradle 提供了多种统一配置手段。推荐优先级从高到低:
- Convention Plugins(推荐) — 将共享配置抽取为 Gradle Plugin,在模块中直接 apply
- subprojects / allprojects 块 — 简单项目可用,但会污染所有模块的命名空间
- 版本目录(Version Catalog) — 统一依赖版本,但参数仍需单独写
- 自定义 Extension — 更灵活,适合复杂参数传递
subprojects 块直接设置 android 参数。改为创建 build-logic 模块(Convention Plugin),并在其中定义 android-common.gradle.kts,然后通过 apply(from = rootProject.file("..")) 或直接作为 included build 引入。
2.6 常见错误与调试
| 错误场景 | 根因 | 解决方案 |
|---|---|---|
Project with path ':library' not found | settings.gradle 未 include 该模块 | 检查 include() 名称是否匹配路径 |
Cannot set 'compileSdk' on a null object | 子模块未应用 android library plugin | 在模块级或 Convention Plugin 中先 apply 插件 |
Build cache invalidated on every change | allprojects 块中修改了不可缓存属性 | 将配置移至 Convention Plugin,并使用 @CacheableTask 优化 |
Dependency on 'classpath' configuration is deprecated | 在 root build.gradle 中使用过时的 classpath 声明 | 全部迁移到 plugins 块 + apply false |
dependencies 块放在 subprojects 或 allprojects 中。这会导致所有模块强制依赖相同库,且无法灵活处理变体(如仅 debug 模块需要特定依赖)。正确的做法是在每个模块的 build.gradle 中按需声明依赖,或通过 Convention Plugin 传递条件性依赖。
2.7 调试配置结构
遇到配置不生效或同步失败时,依次执行以下命令定位问题:
# 查看最终模块的完整配置
./gradlew :app:dependencies --configuration debugRuntimeClasspath
# 输出项目树
./gradlew projects
# 查看模块的 Gradle 属性
./gradlew :app:properties | grep -E "compileSdk|minSdk"
同时,在 Android Studio 的 Gradle Sync 窗口中可以展开每个模块的 build/ 目录,查看中间生成的 build script 缓存文件,这是验证 Convention Plugin 是否正确注入的有效手段。