排查起点: 如果发现构建日志中
---
### 1.2 数据采集:抓取缓存有效性证据
在定位原因之前,必须系统采集缓存系统的运行时数据。以下是最低必要采集清单:
#### 1.2.1 构建扫描(Build Scan)
建议在 Gradle 命令后追加 `--scan` 参数,生成详细的构建报告:
FROM-CACHE 或 UP-TO-DATE 的 Task 数量显著下降,而 EXECUTING 或 CACHE MISS 数量上升,优先怀疑缓存失效。
./gradlew clean assembleDebug --scan
扫描结果中需要重点查看:
- **Task 执行状态**:筛选所有标记为 `EXECUTING` 的 Task,记录其数量与占比。
- **缓存命中率**:`Cache Store` 模块下 `Hits` 与 `Misses` 的比值。
- **输入/输出指纹**:查看引发 Miss 的 Task 输入文件快照是否与预期一致。
#### 1.2.2 缓存目录快照
周期性记录缓存目录的元数据,用于对比异常前后的变化:
# 记录当前缓存文件数量与大小
find ~/.gradle/caches -type f | wc -l
du -sh ~/.gradle/caches
# 检查最近修改的文件(判断是否有异常写操作)
find ~/.gradle/caches -mmin -10 -type f | head -20
#### 1.2.3 构建日志关键关键词
从 `--info` 或 `--debug` 日志中提取以下关键词出现的上下文:
| 关键词 | 含义 |
|--------|------|
| `CACHE MISS` | Task 输出在缓存中未命中 |
| `No caching` | 该 Task 被显式禁用缓存 |
| `Invalidating cache` | 缓存因输入变更或损坏被主动失效 |
| `Reusing` | 缓存命中(正常情况) |
注意: 不要直接删除整个
---
### 1.3 原因定位:三种现象的故障树
下面这张故障树覆盖了三种现象的常见根因与排查路径:
~/.gradle/caches 目录作为“排查第一步”。这会丢失所有本地缓存快照,使对比分析无法进行。应在删除之前先执行快照记录。
排查原则: 遍历树时,优先检查与“依赖解析”相关的重复下载现象,它往往最简单(仓库配置问题),也可能是深层缓存元数据损坏的信号。
---
### 1.4 修复动作与复测
针对三种典型现象,执行以下简要修复动作:
#### 1.4.1 修复动作表
| 现象 | 最小修复动作 | 验证步骤 |
|------|-------------|----------|
| 重复下载依赖 | 清除 `~/.gradle/caches/modules-2` 并重建 | 执行 `./gradlew build --refresh-dependencies`,检查是否仍需远程下载 |
| 编译时间异常长 | 使用 `--rerun-tasks` 后对比全量与增量时间 | 再次执行无参数构建,观察 `UP-TO-DATE` 数量是否恢复 |
| 缓存未命中标记 | 删除特定 Task 的 `build` 输出目录,重新执行 | 检查日志中该 Task 状态是否为 `FROM-CACHE` |
#### 1.4.2 复测标准
每次修复后,应执行两次相同的构建命令(无 clean、无特殊参数),并对比两次的:
1. 构建总耗时。
2. 缓存命中 Task 数量。
3. 是否仍有新依赖下载。
重要: 如果修复后第一次构建正常,第二次构建又出现相同现象,说明缓存系统存在设计层面的根本问题(如输入不确定性或 Task 输出未声明),需要进一步排查 build.gradle 文件中的 Task 配置。
---
### 1.5 预防性检查清单
为避免缓存频繁失效,建议在 CI/CD 和本地开发环境中执行以下预防措施:
- [ ] 使用固定依赖版本(避免 `+`、`latest.release` 等动态版本)
- [ ] 在构建脚本中明确声明 Task 的输入/输出(`inputs.dir()`、`outputs.file()`)
- [ ] 定期检查 `~/.gradle/caches/` 下 `*.lock` 文件数量,超过 50 个建议清理
- [ ] 配置远程缓存(HTTP/HTTPS)时,确保认证凭据有效期大于构建周期
- [ ] 执行 `--write-locks` 生成依赖锁定文件,防止依赖解析结果反复变化
避坑: 不要在 build.gradle 中通过
---
doLast 或 doFirst 动态修改 Task 输出文件而不声明为输出。Gradle 无法追踪隐式输出,缓存将永久失效。