第4章 build.gradle 公共配置抽取
4.1 原理:为什么需要公共配置抽取
在多模块 Gradle 项目中,每个子模块的 build.gradle 常常包含大量重复内容:插件声明、仓库地址、依赖版本号、编译选项等。将公共配置抽取到根项目的 build.gradle 中,可显著减少重复代码,统一项目规范,降低升级维护成本。Gradle 提供三种核心机制来实现公共配置抽取:
- allprojects / subprojects 块 —— 对项目树进行批量配置。
- 插件 apply 方式 —— 在根项目中统一应用插件并传递到子模块。
- ext 扩展属性 —— 定义全局变量,供所有子模块引用。
理解它们的作用域和生命周期,是正确抽取配置的前提。
4.2 allprojects vs subprojects 对比
| 特性 | allprojects | subprojects |
|---|---|---|
| 作用范围 | 当前项目 + 所有子模块 | 仅子模块(不含根项目) |
| 典型用途 | 仓库地址、全局插件 classpath | 子模块共用的编译配置、依赖声明 |
| 配置时机 | 在 evaluation 之前 | 在 evaluation 之前 |
| 风险 | 容易污染根项目的配置 | 不会作用于根项目,更精准 |
4.3 ext 扩展属性详解
ext 是 Gradle 域对象(如 Project)上的额外属性扩展容器。通过 ext 定义的变量具有全局可见性(在同级域内),常用于保存依赖版本、SDK 路径、环境标记等固定值。
// 根 build.gradle
ext {
compileSdk = 34
minSdk = 24
targetSdk = 34
libVersion = "1.2.0"
}
// 子模块中直接引用
android {
compileSdk rootProject.ext.compileSdk
defaultConfig {
minSdk rootProject.ext.minSdk
targetSdk rootProject.ext.targetSdk
}
}
最佳实践:将 ext 定义集中放在根项目的
build.gradle 或单独 gradle.properties 中,避免在多个子模块中重复定义同名属性导致覆盖。
4.4 实现流程:公共配置抽取四步法
- 规划配置分层 —— 区分哪些配置属于所有模块(仓库、插件 classpath),哪些只属于子模块(编译配置、依赖)。
- 定义 ext 全局变量 —— 在根
build.gradle的buildscript.ext或ext块中声明所有版本号和路径。 - 使用 allprojects/subprojects 批量配置 —— 将仓库、公共插件应用放入
allprojects;将编译选项、测试配置放入subprojects。 - 子模块最小化 —— 子模块仅保留模块特有的配置(如 Application 与 Library 差异),通过
apply插件和引用rootProject.ext完成依赖。
完整配置示例(根 build.gradle)
buildscript {
ext {
kotlinVersion = "1.9.0"
agpVersion = "8.2.0"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:$agpVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
subprojects {
apply plugin: 'com.android.library'
android {
compileSdk rootProject.ext.compileSdk
defaultConfig {
minSdk rootProject.ext.minSdk
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
}
}
4.5 调试与常见错误
- ext 属性未定义:子模块引用
rootProject.ext.compileSdk时,若根项目未在 evaluation 前定义 ext,会抛出MissingPropertyException。确保 ext 块放在buildscript外部或在buildscript内部但通过ext.kotlinVersion定义。 - allprojects 中配置冲突:根项目本身也可能应用某些插件,若在
allprojects中重复 apply,会导致配置二义性。使用subprojects避免影响根项目。 - 属性覆盖:子模块中重新定义同名 ext 属性会覆盖根项目值,导致依赖版本不一致。应在子模块中优先引用
rootProject.ext.*,而非重新赋值。
注意:
apply plugin 在 subprojects 块中应用到所有子模块,若某些子模块不是 Android Library(如纯 Java 模块),还需通过条件判断(if (project.name != 'utils'))排除,否则会因缺少 Android 配置而失败。
4.6 性能与稳定性优化
- 避免在
allprojects/subprojects块中执行耗时计算(如读取文件、网络请求),因为每次构建都会执行一次。可将计算结果缓存到 ext 属性中。 - 使用
gradle.properties存储系统级配置(如 JVM 参数),与 ext 互补,减少build.gradle的解析复杂度。 - 将不变配置(如仓库地址)抽入
settings.gradle中的pluginManagement和dependencyResolutionManagement,进一步分离职责。