什么是 Gradle 任务缓存?如何配置本地和远程缓存?
解读
面试官问“任务缓存”并不是想听“把构建结果存起来”这种一句话概念,而是想看你是否真正踩过“CI 排队 20 min、本地反复 clean、远程依赖被墙”这些坑,并给出可落地的国内工程方案。回答时要围绕“输入指纹 → 缓存键 → 命中策略 → 缓存位置 → 失效场景”这条主线展开,同时把国内特色(Maven 镜像、自建 GitLab、混合云、合规审计)带进去,让面试官确信“这人来了就能把我们 15 min 的构建砍到 3 min”。
知识点
- Gradle 任务缓存(Build Cache)本质:把“输入快照 → 输出”映射成键值对,命中后跳过 Task 执行,直接取输出。
- 输入指纹:包括 Task 类名、classpath、@TaskAction 字节码、输入文件哈希、系统属性、环境变量、编译 SDK 版本等;任何字段变化都会换 key。
- 缓存作用域:
- 本地缓存:默认在
<GRADLE_USER_HOME>/caches/build-cache-1,单机复用,生命周期 7 天未命中即淘汰。 - 远程缓存:HTTP 协议,支持 HEAD/PUT/GET,返回 404 视为未命中;Gradle 7+ 支持 Bearer Token、自定义 HTTP 头,方便对接国内私有云。
- 本地缓存:默认在
- 缓存目录结构:
- 本地以“哈希前 2 位/哈希”做两级目录,存储 gzip 的 tar 包;
- 远程以同样哈希做 ObjectKey,兼容 MinIO、阿里云 OSS、腾讯 COS、华为 OBS。
- 安全与合规:
- 缓存包仅含 Task 输出,不含源码,但 AAR/JAR 里可能带 R.class,需开启
buildCache { filter { exclude "/**/*R.class" } }防止泄漏资源 ID。 - 国内金融/车机项目要求“不出内网”,远程缓存必须走 HTTPS + 内网 DNS,且开启
isPush=false做只读镜像。
- 缓存包仅含 Task 输出,不含源码,但 AAR/JAR 里可能带 R.class,需开启
- 失效与调试:
- 加
--info能看到 “Caching disabled for task ‘:app:compileDebugJavaWithJavac’: Caching was not enabled” 原因; - 用
gradle build --build-cache -Dorg.gradle.caching.debug=true打印 key 组成; - 本地调试直接删
caches/build-cache-1验证是否重新执行。
- 加
- 与增量编译关系:任务缓存是“进程级”复用,增量编译是“Task 内”复用;两者互补,不冲突。
- 国内常见误区:
- 把
org.gradle.daemon=false关了,导致每次 fork 新进程,缓存命中率骤降; - 在 CI 里
clean成瘾,把build目录全删,远程缓存形同虚设; - 用 Windows 跑 CI,路径大小写不一致,导致 key 变化。
- 把
答案
Gradle 任务缓存是把 Task 的“输入指纹”作为 key,把输出目录打成 tar.gz 作为 value,下次 key 相同直接解压复用,跳过执行。
配置分三步:
-
启用总开关:
gradle.properties里写org.gradle.caching=true或在 CI 命令行加
--build-cache。 -
本地缓存:默认已开启,路径由
GRADLE_USER_HOME决定;如需改大小或路径,在settings.gradle里写buildCache { local { directory = new File(rootDir, '.gradle/build-cache') removeUnusedEntriesAfterDays = 3 } } -
远程缓存(国内私有云示例):
buildCache { local { enabled = true } remote(HttpBuildCache) { url = 'https://gradle-cache.intra.xxx.com/cache/' credentials { username = System.getenv("CACHE_USER") password = System.getenv("CACHE_PASS") } // 仅 CI 推送,开发者只读 push = System.getenv("CI") == "true" // 国内 OSS 走内网,允许不校验证书(内网可信) allowUntrustedServer = false // 连接超时 30s,防止被墙 connectTimeout = 30000 } }若公司用 MinIO,只需把
url指向https://minio.intra.xxx.com/bucket/,并在 Nginx 加proxy_cache做二级加速。
验证:
本地 ./gradlew :app:assembleDebug --build-cache 第一次 3 min,第二次 40 s,且日志出现 “Reusing output from remote build cache” 即成功。
拓展思考
- 多级缓存拓扑:北京机房写、深圳机房读,如何用 CDN + 回源 MinIO 做到 <100 ms 延迟?
- 缓存污染:A 分支升级了 AGP 8.1,B 分支仍用 7.4,如何设计 key 前缀隔离,避免“旧分支命中新缓存”导致编包异常?
- 合规审计:金融 App 要求“构建过程可回溯”,如何把每次缓存命中记录(taskPath、key、gitCommit、构建人)写入 Elasticsearch,并在审计平台一键溯源?
- 混合编译:Kotlin 1.9 启用 K2 编译器,字节码特征变化,如何让远程缓存平滑灰度,避免全量失效?
- 未来趋势:Gradle 8 引入 Configuration Cache + Build Cache 联动,把 Task 图序列化到磁盘,秒级恢复;国内大型 Monorepo 是否值得一步到位,还是继续拆模块?