Gradle 各版本下载地址大全:镜像加速、离线缓存与故障排查
2026/9/18 16:36:43 网站建设 项目流程

1. 为什么一个"下载"能反复把人卡住

1.1 先说清楚这篇东西到底解决什么问题

Gradle 各版本快速下载地址大全,这个标题听起来像是个资源清单,但我干了这么多年构建工程,越来越觉得它本质上是一份"故障处理手册"——因为绝大多数人搜这个词的时候,电脑上都正挂着一行红色的下载失败提示,而不是在悠闲地做技术选型。

具体场景大概长这样:你克隆了一个别人的项目,敲下gradlew build,终端里进度条爬到 40% 卡死不动,几分钟后甩出一句could not install gradle distribution from ... reason: java.net.SocketTimeoutException。或者你在 Android Studio 里点了 "Sync Project with Gradle Files",然后眼睁睁看着右侧的 Build 窗口十几分钟没有新日志,风扇转得像要起飞。再或者更崩溃一点,你只是想把一个老项目的 Gradle 从 7.6 升到 8.7,结果发现公司内网根本不通外网,构建工具自己都装不上。

这篇文章要做的就是把这些场景一次性理清楚:各个版本的发行包去哪里取、包有几种形态该选哪种、国内有哪些稳定可用的镜像、怎么把分发文件塞进本地让 Wrapper 完全不联网、依赖仓库的镜像怎么配才不会每个项目都改一遍、以及那一堆超时和解析失败的报错到底对应什么根因。内容偏向实操,代码和配置都是可以直接复制的形态,做 Android、Java 后端、Flutter 的同学都能对得上号。

1.2 下载慢这件事,根因其实有三层

很多人以为"慢"是一个问题,实际上它是三个完全不同的问题叠在一起,得分开治。

第一层是Gradle 本身的发行包。Gradle 不是装一次就完了的工具,它是跟着项目走的——每个项目在gradle/wrapper/gradle-wrapper.properties里指定自己要用哪个版本,首次构建时由 Wrapper 去下载对应的 zip 包。这个包 bin 版大概 100MB 出头,all 版接近 200MB,从海外站点拉,网络稍微抖动一下就是几分钟起步。

第二层是项目依赖的 jar 包。这是真正的大头。一个中等规模的 Spring Boot 或 Android 项目,依赖树展开后动辄几百个 jar,加起来几百 MB 到 1GB 都正常。这些包来自 Maven Central、Google Maven 等仓库,同样是海外站点。

第三层是插件与元数据的解析。Gradle 在拉依赖之前,先要去读maven-metadata.xml.pom.module这些描述文件,确定"哪个版本存在、有哪些传递依赖"。这些请求数量极多、单个极小,是最容易被超时打断的环节,也是ModuleVersionResolveException这类报错的高发区。

把这三层分清楚之后,策略就很明确了:发行包尽量走一次性离线落地,依赖包走镜像仓库,元数据解析靠合理的仓库声明顺序 + 缓存复用。后面几章基本就是围绕这三条线展开。

1.3 这篇内容适合谁

如果你是完全没接触过 Gradle 的新手,第一章到第三章能帮你把概念和版本对应关系理清楚,照着做不会踩太离谱的坑;如果你已经被同步超时折磨过好几轮,第四、五、七章是重点,里面有几个技巧(比如手工构造 Wrapper 缓存目录、用初始化脚本全局注入镜像)能省掉大量重复劳动;如果你在做团队级的基础设施,第六章的版本目录和第八章的经验清单更值得看,那部分决定了你的项目在别人机器上能不能一键跑起来。

需要提前说明的是,下面涉及的所有站点地址、版本号、兼容性关系,都是基于公开的官方文档和常见社区实践整理的,具体用的时候请以你访问到的实际目录结构为准。版本这东西更新太快,我写的时候是对的,你读的时候可能已经变了。

2. 动手之前,先搞清楚你要下载的到底是哪个包

2.1 bin、all、src 三种发行包的区别

Gradle 官方为每个版本提供三种主要的分发文件,命名非常规律:

文件名体积量级内容适用场景
gradle-X.Y-bin.zip约 100–130MB可执行程序、核心库,无源码与文档日常构建、CI 环境,首选
gradle-X.Y-all.zip约 180–220MB在 bin 基础上包含源码与 API 文档需要在 IDE 里跳进 Gradle 内部源码调试
gradle-X.Y-src.zip约 60–80MB仅源码研究实现、二次开发

选哪个的答案很直接:除非你明确要在 IDEA 里 F3 跳进 Gradle 的类实现里看逻辑,否则一律选 bin。我见过不少人无脑用 all,理由是"大而全肯定没错",结果就是每个项目首次同步多下 100MB,CI 流水线上十几个 Job 并行跑,白白多出的流量和磁盘占用非常可观。

还有个容易被忽略的点:all包在解压后会多出src目录,如果你在公司内网做统一分发,多出来的这部分会让镜像目录体积膨胀接近一倍。对纯构建场景来说,这部分内容一次都不会被读到。

2.2 校验文件和版本清单文件

每个 zip 旁边都有一个同名的.sha256文件,内容就是一串哈希值。别跳过这一步,理由有三个:一是能确认文件没在下载过程中被截断(大文件断点续传出问题是常事);二是内网镜像同步偶尔会同步到残缺文件;三是 Wrapper 支持distributionSha256Sum参数做强制校验,配置之后如果哈希对不上会直接报错终止,比在构建到一半时冒出诡异的类找不到要好得多。

版本清单文件(通常叫versions.all或类似的索引文件)记录了当前所有可用版本号。你在写脚本批量同步镜像时,用它来遍历比自己去猜版本号靠谱得多。另外官方还提供currentrelease-candidate这类固定别名,用得不多,但知道有这么个东西,看到别人配置里写别名时不会懵。

2.3 Java 版本与 Gradle 版本的对应关系

这是第二个高频踩坑点。Gradle 本身跑在 JVM 上,每个 Gradle 版本对 JDK 有明确的支持区间,装错了就是一句Unsupported class file major version或者一堆莫名其妙的 ASM 报错。下面这张表是基于官方兼容性说明整理的常用区间:

Gradle 版本支持的 JDK 范围(运行 Gradle 自身)
6.x8 – 15
7.0 – 7.28 – 16
7.3 – 7.68 – 19
8.0 – 8.48 – 19
8.5 – 8.78 – 21
8.8 – 8.98 – 22
8.10 – 8.148 – 23
9.0 及以上17 – 24

注意:这里说的是"运行 Gradle 自身"所需的最低/最高 JDK。你项目里编译产物用的 Java 版本是另一回事,通过sourceCompatibilitytargetCompatibility或 toolchain 单独控制。两件事别混。

看到your build is currently configured to use java 21.0.4 and gradle 8.8这类提示,先别慌,多数时候它说的是Android Gradle Plugin 与该 JDK 的组合有问题,而不是 Gradle 本身不支持。Android 侧还有一层 AGP 与 Gradle 的对应关系(比如 AGP 8.x 基本要求 Gradle 8.x 起步),排查时要三条线一起看:JDK 版本、Gradle 版本、AGP 版本。

2.4 一个我反复验证过的版本选择原则

不要在项目里追最新版。Gradle 的大版本更新节奏挺快,小版本之间也可能有行为变更(比如 8.x 里对buildDir的废弃、对配置缓存的收紧)。我的做法是:

  • 新项目:用当前次新的大版本里最新的小版本,比最新版晚一个身位;
  • 存量项目:只在有明确收益时升级,比如需要 JDK 21 支持、需要某个新 API;
  • 团队项目:版本号统一写进版本目录文件,不允许各人在自己机器上私改。

这个原则看起来保守,但它能过滤掉大量"升级完发现某个插件还没适配"的问题。

3. 各版本下载地址的整理逻辑与速查方法

3.1 官方分发地址的命名规律

与其背地址,不如记规律。官方分发地址的结构非常固定:

https://services.gradle.org/distributions/gradle-<版本号>-<包类型>.zip

把你想要的版本号和包类型填进去就是完整地址。比如 8.7 版本的 bin 包,就是版本号填8.7、包类型填bin。理解了这个结构,你就有了"任意版本"的下载能力,而不需要一份别人整理好、随时可能过期的清单——这也是这份"大全"最核心的价值所在。

gradle-wrapper.properties里写这个地址时有个必须注意的细节:

distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip

https:后面的冒号必须转义成\:。原因是 properties 文件格式里冒号是键值分隔符,不转义的话解析器会在冒号处断开,导致 URL 被截断成一个奇怪的值。这个坑非常隐蔽,因为不转义时 Gradle 通常不会立刻报"URL 格式错误",而是给你一个超时或者 404,让你往网络方向查半天。

3.2 国内镜像的路径规律

国内几家云厂商提供了 Gradle 发行包的镜像,路径规律同样是固定前缀加同名文件:

镜像来源地址前缀形式
腾讯云https://mirrors.cloud.tencent.com/gradle/
华为云https://mirrors.huaweicloud.com/gradle/
其他公共镜像站通常在/gradle//gradle/distributions/目录下

用法就是把官方域名整段替换掉,后面的gradle-8.7-bin.zip原样保留:

distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip

提示:镜像站的同步存在延迟,刚发布几天的新版本可能在镜像上还看不到。遇到 404 先回官方地址确认版本是否存在,再判断是不是镜像没同步。别反过来,先怀疑自己写错了。

我在实际使用中总结出一条判断经验:镜像站上如果连gradle-X.Y-bin.zip.sha256这个校验文件都能拿到,说明这个版本同步完整;只有 zip 没有 sha256,往往意味着同步还在进行中,这种时候下下来的包有概率是残缺的,别用。

3.3 一个可以自助生成地址的小脚本

既然地址是拼出来的,那就可以脚本化。下面这段 bash 用来生成一批你关心的版本下载命令,同步到内网镜像目录时很好用:

#!/usr/bin/env bash # 批量生成下载命令,按需修改版本列表与镜像前缀 MIRROR="https://mirrors.cloud.tencent.com/gradle" VERSIONS=("7.6.4" "8.0.2" "8.5" "8.7" "8.10.2" "8.14.3") PKG="bin" for v in "${VERSIONS[@]}"; do file="gradle-${v}-${PKG}.zip" echo "curl -fL --retry 3 -o ${file} ${MIRROR}/${file}" done

-f让 curl 在 404 时直接失败而不是把错误页写成文件,--retry 3处理偶发断连,-L跟随跳转。这三个参数缺一个,你都会在后续校验时收到惊喜。

对应的 Windows PowerShell 版本:

$mirror = "https://mirrors.cloud.tencent.com/gradle" $versions = @("7.6.4","8.0.2","8.5","8.7","8.10.2","8.14.3") foreach ($v in $versions) { $file = "gradle-$v-bin.zip" Invoke-WebRequest -Uri "$mirror/$file" -OutFile $file -MaximumRetryCount 3 }

3.4 版本速查需要关注哪几个点

不用把每个版本都记下来,关注这几个维度就够了:

  • 你项目当前用的版本:直接看gradle-wrapper.properties,这是硬约束;
  • 目标 JDK 版本要求的最低 Gradle 版本:要跑 JDK 21,至少得 8.5;
  • 你用的 AGP 版本要求的 Gradle 区间:这个必须查 AGP 的官方说明,不能猜;
  • 团队其他项目在用的版本:尽量收敛,能少维护一个版本就少一个。

把这四个约束交叉一下,通常能选的版本就剩一两个了。这时候再按"次新大版本 + 最新小版本"的原则挑,基本不会错。

4. 四种落地姿势:从手动安装到完全离线

4.1 姿势一:下载发行包手动安装并配置环境变量

这是最原始也最可控的方式,适合需要在机器上长期使用同一个 Gradle 版本的场景(比如你经常用命令行跑gradle而不是gradlew)。

Windows 上的步骤:

  1. gradle-8.7-bin.zip解压到一个路径不含空格和中文的目录,比如D:\devtools\gradle-8.7。路径里有空格是很多构建脚本翻车的原因,别图省事放"Program Files"。
  2. 新建环境变量GRADLE_HOME,值指向解压出来的目录(注意是解压后的gradle-8.7这一层,不是它的父目录,很多人这里多一层或少一层)。
  3. Path里追加%GRADLE_HOME%\bin
  4. 打开新的命令行窗口(必须新开,旧窗口不会读到新环境变量),执行gradle -v

macOS / Linux 上的步骤:

sudo mkdir -p /opt/gradle sudo unzip gradle-8.7-bin.zip -d /opt/gradle # 加入 shell 配置,注意按你的 shell 选对应文件 echo 'export GRADLE_HOME=/opt/gradle/gradle-8.7' >> ~/.bashrc echo 'export PATH=$GRADLE_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc gradle -v

gradle -v的输出里会同时告诉你 Gradle 版本、Kotlin 版本、JVM 版本、操作系统信息。这四项是排查问题的黄金信息,遇到任何构建问题,先把这个输出贴出来,能省掉一半的来回问询。

注意:不建议在系统里同时配置多个 Gradle 版本到 PATH。真要切换,用版本管理工具或者直接写绝对路径调用,别搞两个都叫gradle的命令互相打架。

4.2 姿势二:改 Wrapper 配置,让项目走镜像

这是日常最常用的方式,改一个文件就行:

# gradle/wrapper/gradle-wrapper.properties distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip distributionSha256Sum=<把对应 sha256 文件的内容粘过来> zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists networkTimeout=60000 validateDistributionUrl=true

几个参数逐个说:

  • distributionPath/zipStorePath:决定包解压和 zip 存哪儿,默认都在用户目录下的.gradle里。如果你的系统盘空间紧张,可以把它指到别的大盘上;
  • distributionSha256Sum:加上之后会强校验。强烈建议加,这是防止拿到残缺包的最有效手段;
  • networkTimeout:毫秒为单位,默认值偏小,网络差的环境下调大能减少无谓失败;
  • validateDistributionUrl:默认开启,会在下载前先探测 URL 是否可达,能更早暴露地址写错的问题。

需要提醒一点:这个文件一旦提交到仓库,就影响到所有协作者。所以在团队项目里改镜像地址之前,最好确认一下:如果有些同事处在一个访问镜像站也不通畅的环境,你是不是该提供两套配置(比如用-P参数传入地址)。

4.3 姿势三:手工构造 Wrapper 缓存目录,实现真正零联网

这是我最想推荐的一个技巧,在内网环境和 CI 缓存预热时特别好用。原理是:Wrapper 下载完成后,会把 zip 和解压结果放在一个由distributionUrl哈希出来的固定目录里,并在旁边放一个标记文件表示"这个包已经就绪"。只要我们把文件按同样的结构放好,并造出标记文件,Wrapper 就会直接跳过下载环节。

目录结构长这样:

~/.gradle/wrapper/dists/ └── gradle-8.7-bin/ └── <URL的哈希值>/ ├── gradle-8.7-bin.zip ├── gradle-8.7-bin.zip.ok ← 关键标记文件 └── gradle-8.7/ ├── bin/ ├── lib/ └── ...

那个哈希值是怎么算的?是对完整的 distributionUrl 字符串做 MD5,取十六进制大写。在 Linux/macOS 上:

printf '%s' 'https://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip' | md5sum

Windows PowerShell 版本:

$url = 'https://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip' $md5 = [System.Security.Cryptography.MD5]::Create() $hash = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($url)) ([System.BitConverter]::ToString($hash)).Replace('-','')

提示:哈希是基于完整 URL 字符串算的,所以你把distributionUrl从官方地址改成镜像地址之后,对应的缓存目录名也会变,需要重新放一份。这是很多人"明明放好了还是不认"的根本原因。

完整的落地脚本可以这样写:

#!/usr/bin/env bash set -euo pipefail URL="https://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip" VER="gradle-8.7" ZIP="gradle-8.7-bin.zip" BASE="$HOME/.gradle/wrapper/dists/gradle-8.7-bin" HASH=$(printf '%s' "$URL" | md5sum | awk '{print toupper($1)}') TARGET="$BASE/$HASH" mkdir -p "$TARGET" cp "$ZIP" "$TARGET/" unzip -q -o "$TARGET/$ZIP" -d "$TARGET" touch "$TARGET/$ZIP.ok" # 必须最后做,避免中途失败留下"已完成"假象 echo "done -> $TARGET"

touch那一步放在最后是有讲究的:如果先创建标记文件再解压,中途失败了,Wrapper 会认为缓存完好,然后在一个残缺目录上跑构建,报出来的错会非常离谱。等所有步骤都成功了再落标记,这是顺序上的硬要求。

4.4 姿势四:依赖仓库换镜像(这个才是提速大头)

发行包解决的是"工具本身",依赖仓库解决的才是"项目内容"。Gradle 的仓库声明可以在三个层级做:

  • 项目级:每个项目的settings.gradlebuild.gradle里改,灵活但重复;
  • 用户级~/.gradle/init.gradle~/.gradle/init.d/下放脚本,一次配置全局生效;
  • 环境变量级:通过GRADLE_USER_HOME指向不同的用户目录,实现"换环境换配置"。

我推荐用户级,理由很简单:你不可能记得住每个项目都去改仓库地址,而一个初始化脚本能管住你机器上所有项目。具体写法放在下一章。

4.5 四种姿势怎么选

姿势适用场景一次性成本后续维护成本
手动安装 + 环境变量命令行常用、CI 基础镜像构建
改 Wrapper 配置日常开发,单个项目低,但需随项目走
手工构造缓存目录内网、CI 预热、完全离线极低
依赖仓库镜像所有场景,提速最明显

实际项目里我基本是三加四组合:Wrapper 走镜像 + 依赖仓库走镜像 + CI 镜像里预置常用版本的缓存目录。三层一起上,新机器上从 clone 到构建成功通常能压到两三分钟以内。

5. 依赖加速:仓库镜像的配置方式与顺序逻辑

5.1 仓库声明的顺序会决定解析结果

Gradle 在找依赖时,是按repositories {}里声明的先后顺序依次查找的。某个坐标如果在第一个仓库找到了,它就不会继续往后找。这个机制带来两个后果:

一是镜像要放前面,否则请求永远先打到慢的源上;二是顺序会影响同名构件——如果两个仓库里存在相同坐标但内容不同的构件(这种情况在某些第三方聚合仓库里真实存在),先声明的那个胜出。

所以配置镜像不是"加进去就行",位置很关键。一个合理的顺序是:内网私服在前、国内公共镜像居中、官方源兜底在后。

// settings.gradle 里统一管理 dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } google() mavenCentral() } }

RepositoriesMode.PREFER_SETTINGS这个设置的含义是"优先使用 settings 里声明的仓库,忽略子模块自己声明的"。它的作用是防止某个第三方模块在它自己的脚本里硬编码一个海外仓库,把你精心配好的镜像顺序打乱。用FAIL_ON_PROJECT_REPOS也可以,更严格,但遇到不规范的第三方库时容易直接构建失败,团队协作场景下PREFER_SETTINGS更稳妥。

5.2 用 init.gradle 做全局注入

这是我最常用的方式,写在~/.gradle/init.gradle

// ~/.gradle/init.gradle allprojects { buildscript { repositories { maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/public' } } } repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/google' } } }

这段脚本会被注入到每一个构建里,包括buildscript块(插件解析)和普通依赖解析,覆盖面最广。有个细节值得说:buildscript和普通repositories两套独立的仓库列表,很多人只改了后者,结果插件下载还是慢——因为插件是从前者解析的。别漏。

settings.gradlepluginManagement又管着另一条路径(插件市场式的插件声明)。三处都照顾到,才算完整:

// settings.gradle pluginManagement { repositories { maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } gradlePluginPortal() google() } }

提示:init.d/目录下所有.gradle文件都会被执行,可以用它来做分环境切换——比如dev-repos.gradleci-repos.gradle,通过环境变量判断后apply from:引入。比在一个大文件里写 if-else 清晰得多。

5.3 缓存、离线模式和缓存清理

Gradle 会把下载过的依赖存到~/.gradle/caches/modules-2/下。这个缓存是按"构件 + 哈希"组织的,同一个依赖被多个项目使用只存一份,这点比早期版本优秀不少。

常用的几个开关:

# 完全离线,只用缓存,网络请求一个都不发 ./gradlew build --offline # 强制刷新,忽略缓存重新解析(排查"明明更新了版本却不生效") ./gradlew build --refresh-dependencies # 只输出依赖树,不下任何东西(排查依赖冲突) ./gradlew :app:dependencies --configuration releaseRuntimeClasspath

--refresh-dependencies是把双刃剑。它能看到最新的版本信息,但代价是把元数据全部重新拉一遍,在慢网络下可能直接卡死。我的习惯是先--offline试(很多时候缓存里就有),不行再联网,联网时也只针对特定模块刷新:

./gradlew build --refresh-dependencies -PrefreshOnly=com.example:lib

然后在脚本里读这个参数做精细化控制。虽然要多写几行,但比全量刷新省下太多时间。

缓存出问题时的清理要精准,不要一上来就rm -rf ~/.gradle——那等于把你所有项目的依赖全部重新下载一遍。正确做法是定位到具体模块目录删掉:

# 只删某个模块的缓存 rm -rf ~/.gradle/caches/modules-2/files-2.1/com.example/lib # 怀疑元数据缓存坏了,删这部分 rm -rf ~/.gradle/caches/modules-2/metadata-*

删完之后 Gradle 会自动重建,只会重新拉那几个构件。

5.4 Gradle 和 Maven 到底差在哪

既然热度里这个词一直挂着,顺手把差异说清楚,因为它直接影响你怎么理解"下载慢"这件事:

维度MavenGradle
配置形式XML,声明式,结构固定Groovy/Kotlin DSL,可编程
构建模型固定生命周期(compile/package/install)任务图(task graph),按需执行
增量构建支持有限完善,配合构建缓存效果明显
依赖范围scope 概念固定configuration 可自定义,更灵活
缓存位置~/.m2/repository~/.gradle/caches/modules-2
学习曲线平缓,但改不动陡峭,但天花板高
老项目迁移成本高,尤其是定制插件多的项目

理解这个差异的价值在于:Maven 的依赖是"扁平声明",你在 pom 里写什么就下什么;Gradle 的依赖解析过程更动态,会受仓库顺序、版本约束、动态版本号、依赖替换规则影响。所以 Gradle 的解析失败往往比 Maven 更难猜——同一个坐标,Maven 拉不到就是拉不到,Gradle 可能是因为前面某个仓库返回了一个"元数据存在但构件不存在"的结果,导致它提前中止了。

6. 版本目录:把版本号从各处收拢到一个文件

6.1 为什么需要 libs.versions.toml

版本目录(Version Catalog)解决的是一个老问题:依赖版本散落在各个模块的构建脚本里,升级一个库要改十几处,还容易漏。

它的做法是抽出一个gradle/libs.versions.toml文件:

[versions] kotlin = "1.9.24" agp = "8.5.2" retrofit = "2.11.0" coroutines = "1.8.1" [libraries] retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

然后在构建脚本里用生成的访问器:

// build.gradle.kts plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) } dependencies { implementation(libs.retrofit.core) implementation(libs.retrofit.gson) implementation(libs.coroutines.core) }

好处很实际:升级 Retrofit 只改[versions]里的一行,所有引用它的模块自动跟着变。而version.ref这种引用方式还能保证同一个库的多个构件版本完全一致,避免出现retrofit是 2.11 而converter-gson是 2.9 这种低级错配。

6.2 和下载地址有什么关系

关系大了。版本目录本身不改变下载行为,但它把"版本"这个变量集中管理之后,你就获得了一个可控的升级点。比如你要验证"换到某个版本能不能解决超时问题",改一行、跑一次,不用满项目搜索替换。

而且在离线场景下,版本目录配合内网私服特别好用:你只要同步目录里出现的这些坐标,不需要同步整个 Maven Central。这在构建一个干净的内网镜像时能省掉几个数量级的工作量。

6.3 Flutter 项目里的插件声明坑

Flutter 项目经常遇到一条警告,大意是"你正在用 apply 脚本的方式命令式地引入 Flutter 的 Gradle 插件,这种方式已废弃,请迁移到声明式的 plugins 块"。

这条警告的根因在android/settings.gradleandroid/app/build.gradle里的历史写法:

// 老写法,会触发警告 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

新版 Flutter 模板改成了:

// android/settings.gradle pluginManagement { def flutterSdkPath = { def properties = new Properties() file("local.properties").withInputStream { properties.load(it) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath }() includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { google() mavenCentral() gradlePluginPortal() } } plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "8.5.2" apply false id "org.jetbrains.kotlin.android" version "1.9.24" apply false }

迁移过程中有两个高频问题:一是local.propertiesflutter.sdk没配或者路径带反斜杠(Windows 上尤其容易),导致includeBuild找不到目录;二是迁移后插件的加载时机变了,某些在apply from之后立刻读取插件配置的代码会失效,需要挪到plugins块之后再执行。

提示:迁移时先把原来的apply from注释掉、新版块加上去,跑一次完整构建确认通过,再删注释。中间态留着,出问题好回退。

7. 常见报错排查手册

7.1 SocketTimeoutException:下载超时

完整报错通常长这样:

Could not install Gradle distribution from 'https://services.gradle.org/distributions/gradle-8.7-bin.zip'. Reason: java.net.SocketTimeoutException: Read timed out

排查顺序建议这样走:

  1. 先确认地址可达。用浏览器或者curl -I直接访问那个 URL,看返回码。如果连不上,就是网络层面的问题,换镜像地址;
  2. 换镜像后确认 hash 目录变化。前面说过,URL 变了哈希就变了,如果之前手工放过缓存,需要重新放;
  3. 调大超时。在gradle-wrapper.properties里加networkTimeout=120000,同时在gradle.properties里加 JVM 层面的超时:
# gradle.properties systemProp.org.gradle.internal.http.connectionTimeout=120000 systemProp.org.gradle.internal.http.socketTimeout=120000
  1. 极端情况下手工落地。用第 4.3 节的方法把包装进缓存目录,一劳永逸。

7.2 ModuleVersionResolveException:依赖解析不到

报错形如:

Caused by: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve gradle:gradle:8.7.

这类问题的关键线索是看冒号前面的坐标属于谁。如果坐标是你项目自己的依赖,说明仓库里确实没有这个版本,或者镜像还没同步;如果坐标是gradle:gradle这种,说明是构建期插件在解析 Gradle 自身的分发,通常意味着插件声明的仓库里缺了对应源。

处理办法:

  • 到镜像站上手动访问对应的目录,确认maven-metadata.xml存在且里面列出了这个版本;
  • 检查是不是pluginManagement里的仓库列表少了一家;
  • 如果是私服,确认私服有没有做"代理远程仓库"的配置,有些私服只代理了 central,没代理 google。

7.3 Java 版本不匹配

典型提示会明确告诉你用了什么 JDK、什么 Gradle 版本。处理路线是:

# 先看当前用的是什么 ./gradlew -v # 明确指定 JDK 运行 Gradle(而不是靠 JAVA_HOME 猜) ./gradlew build -Dorg.gradle.java.home=/path/to/jdk17

或者在gradle.properties里固定:

org.gradle.java.home=C:\\Program Files\\Java\\jdk-17

Windows 路径里的反斜杠要双写或改成正斜杠。这一步之外,更推荐的做法是给项目配toolchain,让 Gradle 自己去管理编译用的 JDK,而不是依赖本机环境:

kotlin { jvmToolchain(17) }

这样即使本机装的是 JDK 21,Gradle 也会自动定位或下载一个 17 来编译,版本一致性问题从根上解决。

7.4 其他几个高频小毛病

现象常见根因处理办法
构建卡在 "Waiting to acquire shared lock"上一个进程没退干净./gradlew --stop停掉守护进程
缓存目录里有.part文件下载中断,残留半成品删掉对应模块目录重新拉
提示 wrapper 校验失败distributionSha256Sum与实际不符重新获取 sha256 值,或暂时移除该配置
换镜像后仍然很慢只改了 Wrapper,没改依赖仓库按第 5 章配置init.gradle
依赖树里出现同一个库多个版本传递依赖版本冲突dependencies任务看树,加resolutionStrategy强制统一
内网构建报 DNS 解析失败内网不通外网,但脚本里还留着官方地址全量替换为内网私服地址

7.5 一个通用的排查动作模板

遇到任何构建问题,我习惯按这四步走,五次里能解决四次:

  1. ./gradlew --version确认工具链版本;
  2. ./gradlew build --offline看是不是纯网络问题(离线能过说明是下载环节);
  3. --info--debug跑一次,看卡在哪一步。--info的输出量大但可读,--debug大到需要重定向到文件再搜关键字;
  4. 定位到具体模块后,只删那一个模块的缓存重试。
./gradlew assembleDebug --info > build-info.log 2>&1 # 然后在 build-info.log 里搜 Downloading / Resolving / FAILED

这个日志文件在跟同事协作排查时也特别有用,直接甩日志比来回描述现象高效得多。

8. 我踩过的坑和几条经验

把所有内容收束到几条我实际用下来最有价值的经验上。

第一条:把"工具下载"和"依赖下载"当成两件事处理。早期我总是混着看,一个项目同步慢,第一反应是"网不行"。后来才意识到,工具包只有一次下载,依赖包才是每天都要碰的。工具包一次搞定离线缓存,依赖包靠镜像,两条线各自优化,效果比笼统地"想办法加速"好太多。

第二条:手工构造 Wrapper 缓存目录这一步,值得花半小时学会。它带来的收益远超预期:CI 镜像里预置三五个常用版本,新流水线冷启动时间能从五六分钟降到几十秒;团队新人入职时把缓存目录拷过去,不用等首次同步。唯一的门槛是要算对哈希、放对文件名、最后落标记文件,注意顺序。

第三条:镜像地址永远配在用户级,不要配在项目级。项目级的配置要跟着代码走,一旦提交上去,就变成对所有人的强制约束,而你并不知道所有人的网络环境。用init.gradle管住自己机器,项目里保留官方地址,这样代码在任何环境都是"标准形态",本地加速是你的私事。

第四条:缓存不要无脑删。我见过同事遇到依赖问题第一反应就是rm -rf ~/.gradle,然后花二十分钟重新下载所有东西,问题还不一定解决。精准定位到模块目录删,是快十倍的方案。判断该删哪一层的方法也很简单:看报错里的坐标,直接映射到modules-2/files-2.1/<group>/<artifact>这个路径。

第五条:版本号和 JDK 的关系,抄一份表放在项目 README 里。这条听起来很土,但它真的省事。新同事接手项目最容易犯的错就是本机 JDK 版本和项目不匹配,README 里写清楚"本项目要求 JDK 17 + Gradle 8.7 + AGP 8.5.2",比等着别人来问要有用得多。配上 toolchain 配置就更稳了,让工具自己去满足约束,而不是靠人的记忆。

第六条:离线构建能力是构建系统的及格线,不是加分项。一个项目如果必须在联网状态下才能构建,那它在 CI 断网、网络抖动、上游仓库临时不可用的时候就会直接瘫痪。花点时间把发行包离线化、把依赖缓存预热好,这件事的价值在平时看不出来,出问题时能救命。

最后补一个容易被忽略的小细节:~/.gradle这个目录默认在系统盘,Windows 上尤其容易撑爆。如果你的开发机系统盘不大,用GRADLE_USER_HOME环境变量把它挪到数据盘,配合第 4.3 节的缓存构造方法一起用,既解决了空间问题,又保留了离线能力。这个环境变量对 Gradle 的所有缓存路径(wrapper、modules、daemon 日志)都生效,挪一次省心很久。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询