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-2Artifact 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 缓存布局流程图

下图展示全局缓存与项目级缓存的完整结构,并突出模块元数据与产物的分离路径:

Gradle 缓存布局概览(离线核心结构) ~/.gradle/caches/ modules-2 transforms-2 build-cache-1 files-2.1 files-2.1/组/模块/版本 hash/ 目录 (实际存放) xxx.jar (产物) xxx.pom (元数据) xxx.module (元数据) 项目 .gradle/ configuration-cache/ buildOutputCleanup/ 离线关键:只保留 modules-2/files-2.1 版本目录中同时包含 .jar .pom .module 才能正确解析

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 可快速验证与修复缓存问题。