前言
K2 编译器把编译阶段砍掉一半时间后,你会发现构建链路的另一段——配置与缓存——才是最拖时间的部分。Gradle 8.x 系列最大的一条主线就是 Configuration Cache(配置缓存),它把"配阶段"独立出来持久化。本篇从工程实战角度拆开讲。
一、Gradle 构建的三个阶段
理解优化之前,先把阶段拆清:
过去几年慢构建的原因,80% 出在 Configuration——每改一个 build.gradle,所有 task 重新计算依赖图。
二、Configuration Cache 怎么工作的
2.1 一句话总结
把"配置"阶段的产物(解析后的 Task DAG、Provider 值)序列化到磁盘。下次构建直接读缓存,跳过解析。
2.2 开启方式
// gradle.properties
org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=warn
开启后第一次会慢一点(要生成缓存),之后明显加速。
2.3 常见不兼容场景
并不是所有 build.gradle 都能直接享受缓存。下面写法会触发"problems":
// 这种写法会让 Configuration Cache 失效
project.afterEvaluate {
println("Hello ${rootProject.name}")
}
// 这种写法也会出问题
val externalFile = file("config.json").also {
logger.lifecycle("Config: ${it.readText()}")
}
核心原则:Configuration Cache 要求所有 Provider 都是"可序列化的 Lazy Provider"。所以:
// 推荐:用 Provider 工厂模式
val configProvider: Provider<String> = project.providers.gradleProperty("app.version")
// 不推荐:在 configuration 阶段直接读文件
val raw = file("version.txt").readText()
三、Build Cache 的分层策略
Configuration Cache 解决"配慢",Build Cache 解决"编慢"。两个一起用才有质变。
// settings.gradle.kts
buildCache {
local {
// 本地缓存,默认在 ~/.gradle/caches/build-cache-1
isEnabled = true
directory = file("${rootDir}/.gradle/build-cache")
}
remote(HttpBuildCache::class) {
url = uri("https://gradle-cache.company.com/")
isPush = true
}
}
实际效果对比(中等规模 Android 工程,3 个 module):
如果你们公司 CI 上每次都重新拉依赖 + clean build,强烈建议搭一个远程 Build Cache(哪怕是局域网内一台机器都行),收益是成倍的。
四、几个让 Configuration Cache 翻车的常见坑
4.1 在 Task 里持有 Project 引用
// 错误:持有了非序列化对象
tasks.register("myTask") {
doLast {
project.someProp // 直接拿 project 在 doLast 里用
}
}
// 正确:把需要的值在 configuration 阶段就解出来
tasks.register("myTask") {
val propValue = providers.gradleProperty("my.prop").get()
doLast {
println(propValue)
}
}
4.2 使用 kotlin.script 动态执行
Kotlin DSL 里动态 eval 一段脚本会破坏缓存。能用 tasks.register 显式声明就别 eval。
4.3 Android Gradle Plugin 特定问题
AGP 8.0 之后才完全兼容 Configuration Cache,但里面有些"隐藏 API"——比如 androidComponents.onVariants { ... } 内部如果用了反射或 mutate Project 内部状态会被警告。
开启 org.gradle.configuration-cache.problems=warn 之后,观察 build 输出,对每个 warning 都用上面的"提早取值"思路改一遍。
4.4 自定义 Plugin 实现
如果你写了内部 Gradle 插件:
abstract class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
// 错误:使用了非 Provider API
val value = project.findProperty("my.prop") as String
// 正确:使用 Provider
val provider = project.providers.gradleProperty("my.prop")
}
}
五、配合 K2 编译器:组合拳收益最大化
K2 编译器带来的编译速度,与 Configuration Cache 带来的配置速度,是叠加的。两者一起用:
// gradle.properties 推荐配置
org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
kotlin.experimental.tryK2=true
某项目实测(60 万行 Kotlin,9 个 module):
六、调试与监控
# 看 Configuration Cache 是否生效
./gradlew assembleDebug --configuration-cache --info 2>&1 | grep "Configuration cache"
# 看 Task 依赖图
./gradlew :app:assembleDebug --dry-run
# 看构建时间分布
./gradlew assembleDebug --profile
# 在 build/reports/profile/ 下生成 HTML 报告
小结
Gradle 8.x 的核心优化方向就两件事:让配置可缓存、让任务结果可共享。日常工程里能立刻做的:开 Configuration Cache、把 build.gradle 里的 findProperty(...).toString() 全部改成 Provider、搭一个远程 Build Cache。
下一篇我们聊 Android 隐私合规——应用市场上架审查越来越严,PIPL(《个人信息保护法》)的合规检查清单,会决定你的产品在国内能不能上架。
参考阅读
Gradle 官方文档:Configuration Cache
Android Developers 博客:Build Cache 实战
Gradle Performance Recipes(社区 PPT 集)