第四章:生成第一个构建扫描(在线版本)
好了,到了真正动手的环节。我知道你肯定等不及了。
Gradle 构建扫描(Build Scan)就像项目的「黑匣子记录仪」。它把你构建过程中每一步耗时、依赖传递、任务并行度……全部记录下来,再以瀑布图的形式呈现出来。我当年第一次看到构建扫描的瀑布图时,心里只有一个感觉:「原来我项目慢在这!」
4.1 什么是构建扫描?为什么非它不可?
构建扫描是 Gradle 提供的在线诊断工具。你运行一次构建,它生成一个唯一的 URL,打开就是一张完整的网络瀑布图。
说白了,它把 Gradle 的「黑盒」拆成了「透明盒」。你能看到:
- 每个 Task 花了多少秒
- 哪些 Task 在并行,哪些在串行等你
- 配置阶段、依赖解析阶段、执行阶段分别多久
我遇到过很多团队,靠直觉猜瓶颈:有人觉得是编译慢,有人觉得是资源下载慢。结果一跑扫描,发现是配置阶段占了 40% 的时间——连编译都没开始呢!
4.2 生成构建扫描的两种方式
从 Gradle 4.3 开始,官方把构建扫描集成进了 Gradle 本身。你不用装任何插件,只需要在命令行加一个参数:
方式一:临时启用
我最推荐新手用这种方式,因为它最直接。在你现有的 Gradle 项目终端里,运行:
./gradlew clean assemble --scan
少写?对,就这么简单。执行完你会看到:
Publishing build scan...
https://gradle.com/s/your-unique-scan-id
点开那个链接,第一次会让你确认邮箱(为了发扫描结果给你),确认完就能看了。
--scan 参数从来不加 clean,因为我想抓增量构建的真实数据。但如果是第一次跑,或者想测全量构建,带上 clean 更准。
方式二:永久配置(适合团队)
如果你想把构建扫描变成团队的默认习惯,可以在 settings.gradle 或 settings.gradle.kts 里加入:
// settings.gradle
plugins {
id 'com.gradle.enterprise' version '3.16'
}
gradleEnterprise {
server = 'https://gradle.com'
buildScan {
publishAlways()
uploadInBackground = false
}
}
这样每次构建都会自动生成扫描,并上传。团队里每个人都能看到,方便对比不同机器的差异。
--scan 按需调用。你想想看,上线前跑一次扫描就够用了。
4.3 第一次看构建扫描的瀑布图
生成完 URL 后,你点进去会看到一个时间线瀑布图。别被密密麻麻的条状图吓到,我教你看几个关键区域:
- 最上方——构建总时间 & 任务总数。一眼就知道这次构建有没有跑偏。
- 左侧栏——任务列表。按时间倒排(最耗时在最上面)。
- 右侧瀑布图——每个任务的开始 & 结束时间。重叠的条表示并行执行,空档就是等待或序列瓶颈。
举个例子:你看到 :app:compileJava 占了 30 秒,但 :lib:compileJava 只占了 5 秒,而且它俩没有重叠——说明你的项目模块依赖没解耦,任务在排队等 CPU。这就是经典的串行瓶颈。
4.4 真正读懂的三个关键指标
| 指标 | 含义 | 我遇到过的问题 |
|---|---|---|
| 配置阶段耗时 | Gradle 解析脚本、构建 DSL 的时间 | 有一次,一个团队的项目用了 200 多个插件,配置阶段占了总时间 60%。我直接建议他们按需加载插件。 |
| 依赖解析耗时 | 下载仓库里 jar、aar 的时间 | 有个项目依赖卡在 mavenCentral 上,换到阿里云镜像后,直接快了 4 倍。 |
| 最大并行度 | 同一时刻最多跑几个任务 | 默认并行数等于 CPU 核心数。如果你看到瀑布图里只有一两条条,说明并行没跑满。 |
你第一次扫出来后,建议先截个图。然后对照这些指标,逐一排查。
4.5 构建扫描的架构关系
下面这张图能帮你理解构建扫描在整个 Gradle 构建流程里的位置:
从图里能看明白:你的 Gradle 进程运行结束后,采集引擎会把性能数据打成一个 JSON「快照」,上传到 Gradle 官方的构建扫描服务。然后你拿到 URL,浏览器从服务端获取数据,渲染成瀑布图。
整个过程对项目代码零侵入。这就是为什么我敢在任何项目上直接加 --scan——出了事也不怕。
4.6 避坑指南:第一次扫描常见问题
- 链接打不开?——检查能不能访问
gradle.com。公司内网可能需要代理,在gradle.properties里配好systemProp.http.proxyHost。 - 构建扫描显示「运行中」一直不结束?——可能是
uploadInBackground = false没设。临时用--scan不会遇到这问题。 - 数据太乱,看不懂?——别急,到第五、六章我会带你一步步拆解瀑布图里的具体信号。
buildScan.publishAlwaysIf(condition) 只在 PR 构建时触发。切记,公开的扫描默认是公开的,如果需要保密,得配置私有仓库或 Gradle Enterprise。
好了,你现在可以立刻跑一次 ./gradlew :app:assembleDebug --scan,拿到你的第一个扫描链接。别怕,数据多翻几遍就熟了。