2. Gradle 默认缓存布局解析:~/.gradle/caches 与项目级缓存、模块元数据与产物分离
2.1 缓存布局的两个层级
Gradle 依赖缓存分为两个物理级别:
- 全局缓存(默认位于
~/.gradle/caches)—— 存储所有项目的依赖 jar、模块元数据、转换产物等。 - 项目级缓存(位于
${projectDir}/.gradle或${projectDir}/build/gradle-cache)—— 存放构建过程的临时文件、本地任务输出等。依赖缓存主要集中于全局层。
离线环境下,全局缓存是复用依赖的唯一来源。理解其内部结构,是建立离线分发、共享缓存、诊断缺失问题的前提。
2.2 全局缓存目录解剖
执行 ls -la ~/.gradle/caches/ 通常看到如下子目录(Gradle ≥ 7.x):
| 子目录 | 用途 | 离线重要性 |
|---|---|---|
modules-2 | 模块依赖文件(jar、aar、pom、module 元数据、哈希索引) | 核心,必须保留 |
transforms-2 | Artifact Transform 的输出(如 Java 类 → Android Dex) | 可重新生成,离线可选缓存 |
jars-3 / jars | 旧版 jar 缓存(已迁移至 modules-2) | 可删除,不影响新构建 |
files-2.1 | 依赖文件(非 jar 类型,如原生库、脚本) | 依据项目使用情况 |
build-cache-1 | 任务输出缓存(构建缓存) | 提升增量速度,非依赖必需 |
modules-2/files-2.1 | (实际 modules-2 内含 files-2.1 子结构) | 同上,模块具体存放 |
🔍 重点:modules-2 是离线依赖缓存的核心。它使用三层目录结构:模块组/模块名/版本/,并在版本目录内分离元数据(.module、.pom)与产物(.jar、.aar)。
2.3 模块元数据与产物分离机制
Gradle 在 modules-2 内将每个版本的依赖拆分为多个独立文件,目的是:
- 元数据(
.module、.pom、.gradle_module_marker)—— 描述传递依赖、变体、属性,不随产物变更而重新下载; - 产物(
.jar、.aar、-sources.jar)—— 二进制内容,可被不同版本元数据引用; - 哈希文件(
.hash、.sha1、.sha256)—— 完整性校验,与产物分离。
以下是一个真实模块的目录布局示例:
~/.gradle/caches/modules-2/files-2.1/
└── com.google.guava
└── guava
└── 31.1-jre
├── 3e3f0f8e2c8f2b4c1d2e3f4a5b6c7d8e9f0a1b2c (实际 hash 目录)
│ ├── guava-31.1-jre.jar ← 产物
│ ├── guava-31.1-jre.pom ← 元数据
│ ├── guava-31.1-jre.module ← Gradle Module Metadata
│ ├── guava-31.1-jre-sources.jar ← 源码
│ └── guava-31.1-jre.jar.sha256 ← 校验文件
├── ... (其他 hash 目录,可能因变体而异)
└── module-remote-versions ← 版本列表缓存
💡 在线状态下,Gradle 优先使用 .module 元数据解析传递依赖;离线时若只保留 .jar 而缺少 .module,可能导致解析失败。因此离线缓存必须完整拷贝对应版本目录下的全部文件。
2.4 项目级缓存及其与全局缓存的关系
项目根下的 .gradle/ 目录(例如 my-app/.gradle/)包含:
configuration-cache/—— 配置缓存;buildOutputCleanup/—— 构建输出清理;fileChanges/—— 文件变更跟踪;gc.properties—— 垃圾回收配置。
它不存储依赖 jar。所有远程依赖始终指向全局缓存。项目级缓存主要存放“当前构建的元内容”,离线时可忽略。
2.5 缓存布局流程图
下图展示全局缓存与项目级缓存的完整结构,并突出模块元数据与产物的分离路径:
2.6 实际操作:验证当前缓存布局
在离线前,建议执行以下命令确认缓存完整性:
# 查看全局缓存顶级目录
ls -la ~/.gradle/caches/
# 查看某个模块的完整缓存(以 guava 为例)
find ~/.gradle/caches/modules-2 -type d -name "guava" | head -3
# 统计缓存总量
du -sh ~/.gradle/caches/modules-2
⚠️ 如果项目使用了 Gradle Module Metadata(.module),请确保 .module 文件存在。缺失会导致离线构建时返回 Could not resolve module,即使 .jar 已存在。
可通过 gradle --offline 测试缓存是否满足全部依赖:
./gradlew build --offline
# 如果有缺失,Gradle 会明确提示具体 artifact 路径
2.7 常见错误与修复策略
| 错误现象 | 根因(与缓存布局相关) | 解决方案 |
|---|---|---|
Could not resolve all files for configuration | 缓存中缺少 .module 或 .pom 文件 | 重新在线下载,或手动从离线源补全对应版本目录 |
Cached artifact is corrupted | .jar 哈希与 .sha256 不匹配 | 删除该哈希子目录,执行 ./gradlew --refresh-dependencies |
Permission denied | 缓存目录被 root 或其他进程锁定 | chmod -R 755 ~/.gradle/caches/modules-2 |
🔧 离线环境中推荐使用 gradle --offline --no-build-cache 以隔离依赖问题,避免构建缓存干扰诊断。
2.8 性能与稳定性优化建议
- 共享全局缓存:将
~/.gradle/caches/modules-2软链接到网络存储,团队共用已下载的依赖。 - 定期压缩:使用
tar -czf offline-cache.tar.gz -C ~/.gradle/caches modules-2打包离线分发包。 - 避免全量清理:不要盲目删除
~/.gradle/caches。只清理transforms-2和build-cache-1可释放空间而不影响依赖解析。 - Gradle 属性配置:在
gradle.properties中设置org.gradle.caching=true以利用构建缓存,但注意离线时需提前预热。
2.9 小结
- 默认缓存布局以
~/.gradle/caches/modules-2为核心,采用“组/模块/版本/hash/”四层目录。 - 元数据(
.module、.pom)与产物(.jar)物理分离,离线时必须完整保留。 - 项目级缓存不参与依赖解析,可忽略。
- 通过
--offline和--refresh-dependencies可快速验证与修复缓存问题。