1. Gradle 构建缓存概述
1.1 缓存的作用
Gradle 构建缓存(Build Cache)是一种用于重用先前构建输出的机制。当启用缓存后,Gradle 会记住任务(Task)的输入(源文件、依赖、配置等)和输出(编译后的类、JAR、报告等),并在后续构建中直接复用这些输出,跳过实际执行。其核心价值在于:
| 维度 | 作用 |
|---|---|
| 加速重复构建 | 同一项目在本地重复构建时,若输入未变,直接拉取缓存结果,节省编译、测试等耗时任务。 |
| 跨机器共享 | 通过远程缓存(Remote Cache),CI 与开发者之间共享构建产物,避免重复计算。 |
| 分支切换友好 | 切换 Git 分支后,许多任务的输入仍相同,缓存可大幅减少重编译量。 |
| 降低 CI 成本 | CI 流水线中若配置远程缓存,不同构建甚至不同 PR 可复用已有结果,缩短等待时间。 |
关键认知:构建缓存不改变构建逻辑,它只是“记住”任务上一次的结果——当输入完全相同时,直接返回上次的输出。这意味着构建缓存是一种确定性优化,不适用于输入永远变化的场景(如每次生成随机版本号)。
1.2 核心术语
掌握以下术语才能准确理解缓存工作原理:
| 术语 | 说明 |
|---|---|
| 构建缓存(Build Cache) | 存储任务输出产物的仓库,分为本地缓存(~/.gradle/caches/build-cache-*)和远程缓存(如基于 HTTP、S3、GCS 的缓存服务)。 |
| 缓存键(Cache Key) | 唯一标识一次任务输入的哈希值。Gradle 根据任务类型、输入文件哈希、依赖版本、构建脚本等综合计算。 |
| 缓存条目(Cache Entry) | 一个缓存键对应的输出产物(一个或多个文件),存储在缓存目录或远程存储桶中。 |
| 缓存命中(Cache Hit) | 当前任务的缓存键在缓存中已存在,Gradle 直接下载/复制输出,跳过任务执行。 |
| 缓存未命中(Cache Miss) | 缓存键不存在,Gradle 必须实际执行任务,并将结果写入缓存供后续使用。 |
| 本地缓存 | 开发者机器上的缓存目录,默认自动启用(即使未显式开启)。 |
| 远程缓存 | 共享的缓存服务,需额外配置。支持 HTTP、S3、GCS 等后端。 |
1.3 缓存与增量编译的区别
增量编译(Incremental Compilation)和构建缓存是两个互补但不同的概念。很多人容易混淆,下面通过表格对比:
| 维度 | 构建缓存 | 增量编译 |
|---|---|---|
| 粒度 | 任务级——整个任务(如 compileJava)的输出被缓存。 | 文件级——仅重新编译发生变化的源文件,未变的类文件保留。 |
| 持久化 | 输出可跨构建、跨机器持久保存。 | 仅在同一构建会话内有效(依赖 Gradle Daemon 的内存状态)。 |
| 适用场景 | 任何任务,包括编译、测试、打包、代码生成等。 | 主要针对编译任务(Java、Kotlin、Scala 等)。 |
| 共享能力 | 支持远程共享,CI 可复用开发者构建结果。 | 不可共享,仅本地有效。 |
| 触发条件 | 任务输入哈希变化。 | 源文件内容、注解处理器、编译选项等变化。 |
| 是否可替代 | 不能替代增量编译,两者协同工作:增量编译减少单次构建内的编译量,缓存避免多次构建间的重复执行。 |
常见误解:很多人以为“开启缓存就能完全取代增量编译”。实际上,缓存需要远程/本地存储读写,对于快速增量变更(比如改一行代码),增量编译几乎无代价,而缓存可能需要序列化/反序列化。现代 Gradle 项目通常同时启用两者,达到最优效果。
1.4 适用场景
并非所有项目都适合大范围使用构建缓存。判断是否应该启用缓存,可对照以下清单:
- 推荐使用
- 多模块项目,模块间依赖频繁但变更面较窄
- CI 流水线中多分支并行构建,同一任务重复执行概率高
- 本地开发者频繁切换分支或拉取新代码
- 使用大型框架(如 Spring Boot、Android),编译耗时较长
- 谨慎使用或需调优
- 任务输入高度动态(如每次生成新 UUID、时间戳)
- 任务输出巨大(如数百 MB 的 Docker 镜像),缓存存储/传输成本高
- 远程缓存网络延迟高,导致缓存下载比本地执行更慢
- 某些自定义任务未正确声明输入/输出,导致缓存无效或错误复用
- 不建议使用
- 仅单次构建、无重复构建需求的临时性项目
- 无共享需求且构建时间本身极短的纯脚本项目
1.5 启用方式与简单配置
从 Gradle 4.0 开始,构建缓存是默认开启的(仅本地)。要强制启用或配置远程缓存,可通过以下方式:
命令行方式(单次构建生效)
./gradlew build --build-cache
全局配置文件(~/.gradle/gradle.properties 或项目内 gradle.properties)
org.gradle.caching=true
远程缓存配置示例(settings.gradle.kts)
buildCache {
remote(HttpBuildCache) {
url = uri("https://cache.example.com/")
push = true // 是否允许向远程推送缓存条目
}
}
本地缓存路径(默认)
~/.gradle/caches/build-cache-1
调试小技巧:添加
日志中会出现
--info 或 --debug 日志可观察缓存行为:$ ./gradlew clean assemble --build-cache --info | grep "build cache"日志中会出现
Cache hit for task 或 Cache miss for task 等关键信息。
1.6 构建缓存工作流程(核心原理示意)
下图展示了 Gradle 在一次构建中如何与本地和远程缓存交互。当任务执行前,Gradle 计算缓存键,依次查询本地缓存、远程缓存;若命中则直接取用输出,否则执行任务并回填缓存。
上图展示了典型的缓存决策路径:Gradle 先根据任务输入计算出缓存键,然后优先查询本地缓存,若本地缺失则远程查询;若远程命中则下载到本地并复用;若全部未命中,则执行任务并将输出同时写入本地和远程缓存(如果允许推送)。