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 构建缓存工作流程 源文件 / 输入 任务执行前计算 缓存键 (Hash) 本地缓存 远程缓存 命中 → 复用输出 未命中 → 执行任务 并写入缓存 构建输出产物 虚线表示未命中时 任务输出被写入 本地及远程缓存

上图展示了典型的缓存决策路径:Gradle 先根据任务输入计算出缓存键,然后优先查询本地缓存,若本地缺失则远程查询;若远程命中则下载到本地并复用;若全部未命中,则执行任务并将输出同时写入本地和远程缓存(如果允许推送)。

1.7 本章小结