## 1.1 缓存失效的三种典型现象 在 Gradle 构建过程中,缓存失效并不会直接弹出错误提示,而是通过一系列**反常行为**暴露出来。开发者需要具备“从现象反推缓存”的诊断意识。本章聚焦三种最频发的缓存失效表现: | 现象 | 典型表现 | 影响范围 | 初步判断 | |------|----------|----------|----------| | **重复下载依赖** | 每次 `./gradlew build` 都重新下载相同的 JAR、AAR 文件 | 所有依赖项 | 远程缓存或本地缓存未命中 | | **编译时间异常长** | 增量编译时间接近甚至超过全量编译,Task 状态始终显示 `EXECUTING` | 编译类、资源处理 Task | 输入/输出哈希变更或缓存目录损坏 | | **缓存目录未命中标记** | `~/.gradle/caches/` 下文件虽在,但构建日志频繁出现 `CACHE MISS` | 局部或全局 | 缓存元数据损坏、标识符不匹配 |
排查起点: 如果发现构建日志中 FROM-CACHE 或 UP-TO-DATE 的 Task 数量显著下降,而 EXECUTING 或 CACHE MISS 数量上升,优先怀疑缓存失效。
--- ### 1.2 数据采集:抓取缓存有效性证据 在定位原因之前,必须系统采集缓存系统的运行时数据。以下是最低必要采集清单: #### 1.2.1 构建扫描(Build Scan) 建议在 Gradle 命令后追加 `--scan` 参数,生成详细的构建报告:
./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` | 缓存命中(正常情况) |
注意: 不要直接删除整个 ~/.gradle/caches 目录作为“排查第一步”。这会丢失所有本地缓存快照,使对比分析无法进行。应在删除之前先执行快照记录。
--- ### 1.3 原因定位:三种现象的故障树 下面这张故障树覆盖了三种现象的常见根因与排查路径: 缓存失效现象识别 - 决策树 缓存失效现象发生 重复下载依赖 编译时间异常长 缓存目录未命中标记 远程仓库 认证失败 本地缓存 目录损坏 输入快照 哈希变动 增量编译 元数据损坏 构建脚本 动态版本 任务输出 目录重建 说明:每个一级分支下仅列出了最可能的根因。 实际排查时需要结合日志关键词与缓存快照进一步确认。 逐层下钻,结合日志与扫描数据验证 检查:仓库配置 凭据与URL 检查:输入指纹 与增量日志 检查:任务输出 缓存标识符 进入第2章:数据采集与日志分析
排查原则: 遍历树时,优先检查与“依赖解析”相关的重复下载现象,它往往最简单(仓库配置问题),也可能是深层缓存元数据损坏的信号。
--- ### 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 无法追踪隐式输出,缓存将永久失效。
---