配置缓存适配
源:Gradle 官方文档 - Configuration Cache
配置缓存通过序列化配置结果并跳过配置阶段,实现50%+的构建速度提升。
配置缓存原理
什么是配置缓存
Gradle 构建生命周期:
- Initialization(初始化)
- Configuration(配置)← 配置缓存跳过这里
- Execution(执行)
传统构建流程:
bash
$ ./gradlew build
> Configuring project # 配置阶段(每次都执行)
> Task :app:compile
> Task :app:test
BUILD SUCCESSFUL配置缓存后:
bash
$ ./gradlew build
> Reusing configuration cache. # 跳过配置!
> Task :app:compile UP-TO-DATE
> Task :app:test UP-TO-DATE
BUILD SUCCESSFUL性能提升
典型项目:
- 配置阶段:5-15 秒
- 执行阶段:20-60 秒
开启配置缓存后:
- 配置阶段:< 1 秒(从缓存加载)
- 执行阶段:20-60 秒(不变)
- 总提升:20-50%
启用配置缓存
gradle.properties 配置
properties
org.gradle.configuration-cache=true命令行启用
bash
./gradlew build --configuration-cache问题报告模式
properties
# 遇到问题时失败(推荐)
org.gradle.configuration-cache.problems=fail
# 或仅警告
org.gradle.configuration-cache.problems=warn兼容性要求
核心限制
配置缓存要求任务在执行阶段不能访问配置阶段的对象:
禁止访问的对象:
projectrootProjectallprojectssubprojectsparent
为什么有这些限制
原因:配置缓存序列化任务配置,而 project 对象无法被序列化。
常见违规代码
违规 1:在 doLast 中访问 project
❌ 错误代码:
kotlin
tasks.register("printName") {
doLast {
println(project.name) // 违规!
}
}问题:project 是配置对象,无法序列化。
✅ 修复方案:
kotlin
tasks.register("printName") {
val projectName = project.name // 配置阶段获取
doLast {
println(projectName) // 执行阶段使用
}
}或使用 Provider:
kotlin
abstract class PrintNameTask : DefaultTask() {
@get:Input
abstract val projectName: Property<String>
@TaskAction
fun execute() {
println(projectName.get())
}
}
tasks.register<PrintNameTask>("printName") {
projectName.set(project.name)
}违规 2:在任务中访问其他 project
❌ 错误代码:
kotlin
tasks.register("checkVersion") {
doLast {
val coreVersion = project(":core").version // 违规!
println(coreVersion)
}
}✅ 修复方案:
kotlin
tasks.register("checkVersion") {
val coreVersion = project(":core").version
doLast {
println(coreVersion)
}
}违规 3:使用 buildDir
❌ 错误代码:
kotlin
tasks.register("generateFile") {
doLast {
val output = File(project.buildDir, "output.txt") // 违规!
output.writeText("data")
}
}✅ 修复方案:
kotlin
abstract class GenerateFileTask : DefaultTask() {
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun execute() {
outputFile.get().asFile.writeText("data")
}
}
tasks.register<GenerateFileTask>("generateFile") {
outputFile.set(layout.buildDirectory.file("output.txt"))
}违规 4:配置阶段执行命令
❌ 错误代码:
kotlin
// 配置阶段执行
val gitHash = "git rev-parse HEAD".execute().text
tasks.register("printHash") {
doLast {
println(gitHash)
}
}✅ 修复方案:
kotlin
tasks.register("printHash") {
val gitHash = providers.exec {
commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText
doLast {
println(gitHash.get())
}
}Lazy API 适配
使用 Provider API
核心原则:使用 Provider 延迟计算。
传统方式 vs Lazy API:
kotlin
// ❌ 传统方式
val version = project.version.toString()
// ✅ Lazy API
val version = providers.provider { project.version.toString() }Property 类型
常用 Property:
kotlin
abstract val stringProp: Property<String>
abstract val intProp: Property<Int>
abstract val fileProp: RegularFileProperty
abstract val dirProp: DirectoryProperty
abstract val listProp: ListProperty<String>
abstract val mapProp: MapProperty<String, String>示例:完全兼容的任务
kotlin
@CacheableTask
abstract class ProcessTask : DefaultTask() {
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val inputFile: RegularFileProperty
@get:Input
abstract val version: Property<String>
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun execute() {
val input = inputFile.get().asFile.readText()
val result = "$input (version: ${version.get()})"
outputFile.get().asFile.writeText(result)
}
}
tasks.register<ProcessTask>("process") {
inputFile.set(file("input.txt"))
version.set(project.version.toString())
outputFile.set(layout.buildDirectory.file("output.txt"))
}诊断和修复
运行诊断
bash
./gradlew build --configuration-cache输出示例:
Configuration cache entry stored.
3 problems were found storing the configuration cache.
- Task :app:myTask of type MyTask: cannot serialize object of type 'org.gradle.api.internal.project.DefaultProject'
- Task :app:otherTask: invocation of 'Project.getBuildDir()' at execution time
See the complete report at file:///path/to/report.html查看详细报告
报告位置:build/reports/configuration-cache/<hash>/configuration-cache-report.html
报告内容:
- 问题列表
- 问题位置(文件和行号)
- 建议修复方案
插件兼容性
检查插件兼容性
bash
./gradlew help --configuration-cache常见插件兼容性:
| 插件 | 兼容性 | 备注 |
|---|---|---|
| Android Gradle Plugin 8.0+ | ✅ | 完全支持 |
| Kotlin Plugin 1.8+ | ✅ | 完全支持 |
| Hilt | ✅ | 需要最新版本 |
| Room | ✅ | KSP 版本支持 |
| Firebase | ⚠️ | 部分版本支持 |
不兼容插件的处理
临时禁用配置缓存:
kotlin
configurations.all {
if (!gradle.startParameter.isConfigurationCacheRequested) {
// 仅在未启用配置缓存时执行
}
}配置缓存与构建缓存
区别对比
| 特性 | 配置缓存 | 构建缓存 |
|---|---|---|
| 跳过阶段 | Configuration | Execution |
| 缓存内容 | 任务配置 | 任务输出 |
| 速度提升 | 20-50% | 30-80% |
| 兼容性要求 | 高 | 低 |
| 推荐组合 | ✅ 同时使用 | ✅ 同时使用 |
同时启用
properties
# gradle.properties
org.gradle.configuration-cache=true
org.gradle.caching=true
org.gradle.parallel=true效果叠加:
- 配置缓存:跳过配置阶段
- 构建缓存:复用任务输出
- 并行构建:同时执行多个任务
- 总提升:50-90%
实战案例
案例1:Android 项目适配
before:
kotlin
// build.gradle.kts
val flavor = project.findProperty("flavor") as? String ?: "dev"
android {
productFlavors {
create(flavor) {
// 配置
}
}
}after:
kotlin
val flavor = providers.gradleProperty("flavor")
.orElse("dev")
android {
productFlavors {
create(flavor.get()) {
// 配置
}
}
}案例2:自定义任务适配
before:
kotlin
tasks.register("generateConfig") {
doLast {
val env = project.findProperty("env") as? String ?: "dev"
File(project.buildDir, "config.json").writeText("""{"env": "$env"}""")
}
}after:
kotlin
abstract class GenerateConfigTask : DefaultTask() {
@get:Input
abstract val environment: Property<String>
@get:OutputFile
abstract val configFile: RegularFileProperty
@TaskAction
fun execute() {
val config = """{"env": "${environment.get()}"}"""
configFile.get().asFile.writeText(config)
}
}
tasks.register<GenerateConfigTask>("generateConfig") {
environment.set(
providers.gradleProperty("env").orElse("dev")
)
configFile.set(layout.buildDirectory.file("config.json"))
}案例3:Git 信息集成
before:
kotlin
val gitHash = "git rev-parse HEAD".execute().text.trim()
android {
defaultConfig {
buildConfigField("String", "GIT_HASH", "\"$gitHash\"")
}
}after:
kotlin
val gitHash = providers.exec {
commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText.map { it.trim() }
android {
defaultConfig {
buildConfigField("String", "GIT_HASH", "\"${gitHash.get()}\"")
}
}最佳实践
使用 Lazy API:
- Provider/Property 代替直接值
- exec Provider 执行命令
- fileContents Provider 读取文件
避免访问 project:
- 配置阶段提取值
- 使用 layout API
- 使用 providers API
声明输入输出:
- @Input/@Output 注解
- 支持增量构建
- 支持缓存
测试兼容性:
bash
./gradlew build --configuration-cache --configuration-cache-problems=failCI 强制启用:
properties
org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=fail监控效果:
- 使用 Build Scan
- 查看配置时间
- 对比前后性能