升级前评估:当前项目使用的 Gradle 版本归纳总结
升级 Gradle 最怕什么?最怕一拍脑袋就改版本号,然后整个项目崩给你看。我经历过太多次这样的“惊喜”——明明只是改了个小版本,结果编译时报出一堆看不懂的异常,调试两天才发现是某个插件的兼容性问题。所以,升级前先摸清家底,这件事比写代码本身还重要。
为什么要做版本评估?
说白了,Gradle 本身和它的插件、构建脚本、甚至 JDK 版本之间都存在微妙的依赖关系。你想想看,升级一个构建工具,影响的可是整个构建管道。我不止一次在项目里看到:gradle-wrapper.properties 里写了 7.4,但 build.gradle 里却用了只在 6.x 上稳定的插件语法,结果一跑就报错。这种事很常见。
所以我的习惯是:在动手升级前,先画一张“版本关系图”。把当前项目里所有跟 Gradle 版本相关的要素列清楚,然后逐一评估升级带来的影响。下面这个流程就是我平时用的方法:
第一步:翻查 Gradle Wrapper
项目里最直接的版本信息藏在 gradle/wrapper/gradle-wrapper.properties 里。打开它,找到 distributionUrl 这一行:
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
这里的 7.4.2 就是当前项目正在使用的 Gradle 发行版。但注意,这仅仅是“Wrapper 指定的版本”。实际运行时还可能被本地安装的 Gradle 覆盖?我个人习惯在终端里跑一句 ./gradlew --version 来确认真正生效的版本:
$ ./gradlew --version
------------------------------------------------------------
Gradle 7.4.2
------------------------------------------------------------
Build time: 2022-03-31 15:36:47 UTC
Revision: d5ad7e120eb1ef2dc4b4b5ff1c08b5f0f7a7f7f7
Kotlin: 1.5.31
Groovy: 3.0.9
Ant: Apache Ant 1.10.11
JVM: 11.0.15 (Oracle Corporation 11.0.15+10)
OS: Mac OS X 11.6
输出里最关键的几个信息:Gradle 版本、Kotlin 版本、JVM 版本。这三个东西往往需要一起升级。
gradle-wrapper.properties 一致。我曾经见过因为某位同事本地手动改了版本号没提交,导致 CI 和本地构建结果不一致,排查了整整一个下午。
第二步:盘点项目中的插件和依赖
Gradle 自身升级后,影响最大的是生态中的插件——尤其是 Android Gradle Plugin (AGP)、Kotlin 插件、以及各种第三方构建插件。我一般会列一张表,记录当前每个核心插件的版本:
| 插件名称 | 当前版本 | 目标 Gradle 版本兼容范围 | 备注 |
|---|---|---|---|
| com.android.application | 7.2.1 | Gradle 7.0+ | 建议一同升级到 AGP 7.4+ |
| org.jetbrains.kotlin.android | 1.6.21 | Gradle 7.0+ (Kotlin 1.6 需 JDK11+) | Kotlin 1.8 开始要求 Gradle 7.6+ |
| com.google.dagger.hilt.android | 2.44 | Gradle 7.0+ | Hilt 2.44 兼容性较好 |
| io.realm | 10.11.1 | Gradle 7.3+ | Realm 对 Gradle 版本较敏感 |
你看,每个插件都有自己要求的 Gradle 最低/最高版本。如果直接升级 Gradle 到 8.0,可能某个插件还没适配,就需要同时升级插件或者找替代方案。我记得有一次在升级到 Gradle 7.6 时,项目里用的一个古老版本的 Kotlin 插件报错说“不支持的 Gradle API”,最后不得不连升三个版本的 Kotlin 才解决。
第三步:检查构建脚本语法变更
Gradle 每个大版本都会废弃或移除一些 API。我习惯在升级前先看看 Gradle 官方 Release Notes 中的“Deprecations”部分。比如从 7.x 升级到 8.x,最明显的变化就是:
compile、runtime配置名称被彻底移除(已在 7.x 废弃)- Project 的
buildscript()闭包行为有调整 - 某些 Gradle API 在 8.x 中被标记为
@Internal,自定义 Task 需要适配
另外,别忘了看看 settings.gradle 里有没有使用 pluginManagement、dependencyResolutionManagement 这些新语法。我在一个老项目里发现 settings.gradle 还用的旧式 apply plugin 方式,升级 Gradle 后直接编译失败。
./gradlew clean)。我曾经掉过一个坑:升级后直接跑 ./gradlew build,结果因为缓存了旧的 Gradle 版本编译产物,报了一堆诡异的链接错误。清掉 .gradle/caches 再重新构建才解决。
制作版本兼容性矩阵
把所有信息收集完之后,我通常会用一张矩阵图来可视化“当前版本 → 目标版本”的升级路径。下面是一个简化的例子:
| 组件 | 当前版本 | 目标版本(Gradle 8.2) | 需要同步升级? |
|---|---|---|---|
| Gradle | 7.4.2 | 8.2 | — |
| AGP | 7.2.1 | 8.1.0 | 是(AGP 8.x 配合 Gradle 8.x) |
| Kotlin | 1.6.21 | 1.9.0 | 是(1.6 不支持 Gradle 8.x) |
| JDK | 11 | 17 | 建议升级(Gradle 8.x 推荐 JDK17) |
| Hilt | 2.44 | 2.48 | 是 |
这张表做出来之后,你就可以按图索骥,逐项升级。注意,升级顺序也有讲究——我一般先升级 JDK,再升级 Gradle,接着升级 AGP/Kotlin,最后升级其他第三方插件,每升级一项就构建一次,确保不会一次性积累太多问题。
避坑指南:我踩过的那些雷
这部分全是真金白银的经验,你看完也许能省下半天调试时间:
- 不要跳过小版本。 我曾尝试从 Gradle 7.2 直接跳到 8.0,结果 AGP 7.2 根本不支持,连报错信息都不友好。建议至少先升级到 7.6 过渡一下。
- 检查 Gradle Daemon 状态。 有时升级后旧 Daemon 还在运行,导致新版本没生效。执行
./gradlew --stop重启 Daemon 再试。 - 关注 Gradle 插件发布的 compatibility matrix。 比如 AGP 8.x 明确要求 Gradle 8.x,但有些三方插件(比如 Realm、Dagger Hilt)可能还停在试验性支持阶段。
- 配置中的
compileSdk和targetSdk也可能需要同步调整。 这不是 Gradle 直接管的事,但升级 AGP 后编译行为会变,比如默认启用了 R8 混淆规则的新格式。