前言
CI 上经常会给 Gradle 传一些临时参数,比如版本号、渠道号、构建开关。
CI 上经常会授予 Gradle 传输一些临时参数、比如版本号、渠道号、建设开关。
这些参数如果只是 task 执行阶段才用,本来不应该影响配置缓存。但在旧版本里,通过 -Dorg.gradle.project.* 或 ORG_GRADLE_PROJECT_* 传进来的项目属性,只要值变了,就可能让 Configuration Cache 重新计算。
这些参数如果只是任务执行阶段才用的,本不应该影响配置缓存。但在旧版本本里,通过-Dorg.gradle.project。* 或 ORG_GRADLE_PROJECT_* 传统项目属性,只需要更改值,就可以进行配置缓存重新计算。
Gradle 9.6.0最值得看的就是这个配置缓存修正。CI 参数多的 Android 项目,会比单模块本地构建更容易感知到。
Gradle 9.6.0 最有价值的看点就是这个部署缓存修正。CI 参数多的 Android 项目,会比单模块本地结构建更容易感知到。

参数变了,缓存没必要失效
Gradle 的 Configuration Cache 缓的是配置阶段结果。以前有一个比较烦的点:项目属性通过系统属性或环境变量传进来时,只要这些值变化,就可能让配置缓存失效,即使这个属性并没有在配置阶段被读取。
Gradle 的配置缓存的缓慢是配置阶段的结果。以前有一个比较麻烦的点:项目属性通过系统属性或环境变量传入时,只需要这些值的变化,就可以使配置缓存失效,即使这个属性并没有在配置阶段被读取。
先看一个最小例子:
bash
tasks.register("printValue") {
val value = providers.gradleProperty("value").orElse("N/A")
doLast {
println("value: ${value.get()}")
}
}
这里的 value 是在 task 执行阶段读取,不是在配置阶段读取。
这里的值是在任务执行阶段读取,而不是在部署阶段读取。
旧版本里,如果通过 -Dorg.gradle.project.value=1 或 ORG_GRADLE_PROJECT_value=1 传值,下一次改成别的值,Gradle 可能会认为缓存不可复用。
旧版本本里,如果通过-Dog.gradle.project.value=1 或 ORG_GRADLE_PROJECT_value=1 传值,下一次改造别的值,Gradle 可能会认可为缓存,不可复用。
Gradle 9.6.0 会识别这个属性没有参与配置阶段,所以下面这种命令可以继续复用配置缓存:
Gradle 9.6.0 会识别这个属性没有参与配置阶段,所以下面这种命令可以继续续用于配置缓存:
bash
./gradlew --configuration-cache printValue -Dorg.gradle.project.value=2
ORG_GRADLE_PROJECT_value=3 ./gradlew --configuration-cache printValue
对 Android 项目来说,典型场景就是 CI 构建号、渠道包参数、是否上传 mapping、是否开启某个发布动作。这些值经常每次构建都不同,但很多时候只在 task action 里用。
如果只是执行阶段读取,Gradle 9.6.0 不会因为值变了就重新算 task graph。
这个优化不会帮项目自动修好所有配置缓存问题。自定义 task 如果在配置阶段读取环境变量,或者插件在配置阶段做了 I/O,缓存还是会失效。9.6.0 修的是"没有被配置阶段读取的项目属性被过度追踪"这个点。

CI 命令别等输入
另一个和 CI 直接相关的改动是 --non-interactive。这个参数会关闭交互式控制台提示,适合没有用户输入的流水线和脚本。
在 CI 里可以这样写:
bash
./gradlew --non-interactive --configuration-cache :app:assembleRelease
它解决的不是构建速度,而是自动化环境里的卡住问题。构建命令如果进入等待输入的状态,CI 上通常只会表现成超时,日志里还不一定能第一时间看出原因。
bash
NO_COLOR=1 ./gradlew --non-interactive test
NO_COLOR 也适合放在 CI 里。当环境变量 NO_COLOR 非空时,Gradle 会关闭颜色输出。日志被平台采集后,不会混进太多控制字符。
测试报告也补了一个小功能。Test task 生成的 HTML 报告里,表格列头可以点击排序;测试数、失败数、跳过数、耗时这些数字列默认先按降序,成功率默认先按升序。多模块项目里查慢测试和失败类会省一点时间。

root 变量别再偷读
这次有一个会影响老 Gradle 脚本的废弃项:子工程里隐式查找父工程属性和方法开始报警,Gradle 10 会移除这个行为。
Groovy DSL 里很容易写出这种脚本:
bash
// root build.gradle
ext.foo = "hello"
// child/build.gradle
println(foo)
child/build.gradle 里没有定义 foo,Gradle 会往父工程找,最后从 root project 的 ext.foo 解析出来。这个行为过去能跑,但项目大了以后很容易留下隐藏依赖。拼错一个名字,也可能刚好命中父工程里的同名属性。
Gradle 9.6.0 开始,隐式引用和 findProperty()、property()、hasProperty() 这类 API 如果从父工程解析到值,都会给出废弃警告。
共享配置要挪到明确位置。简单值放到 gradle.properties:
bash
compileSdk=36
minSdk=26
Kotlin DSL 里通过 Provider 读取:
bash
val compileSdkVersion = providers.gradleProperty("compileSdk")
.map(String::toInt)
android {
compileSdk = compileSdkVersion.get()
}
更复杂的约定配置放到 convention plugin 里。比如 Android library 模块统一配置 compileSdk、minSdk、Kotlin 编译参数,不要靠每个子模块从 root project 隐式拿值。
处理完这些警告后,在 settings.gradle.kts 里提前启用 Gradle 10 行为:
bash
enableFeaturePreview("NO_IMPLICIT_LOOKUP_IN_PARENT_PROJECTS")
这条对 Android 老项目比较实用。很多项目早年从单模块拆到多模块,root project 的 ext、subprojects、allprojects 里堆了不少变量。Gradle 9.6.0 不会马上让构建失败,但它已经把迁移窗口打开了。

升级方式
升级方式还是改 Wrapper:
bash
./gradlew :wrapper --gradle-version=9.6.0
./gradlew :wrapper
执行完成后,gradle/wrapper/gradle-wrapper.properties 会指向新的 Gradle 分发包:
bash
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
兼容性上,Gradle 9.6.0 运行 Gradle 本身需要 JVM 17 到 26。内置 Kotlin 是 2.3.21,Kotlin DSL 的语言版本仍是 2.2。Android 侧,Gradle 9.6.0 已测试的 AGP 范围是 9.0 到 9.3.0-alpha06。

最后
Gradle的版本你们都升级到多少了?