第四章:生成第一个构建扫描(在线版本)

好了,到了真正动手的环节。我知道你肯定等不及了。

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 后,你点进去会看到一个时间线瀑布图。别被密密麻麻的条状图吓到,我教你看几个关键区域:

  1. 最上方——构建总时间 & 任务总数。一眼就知道这次构建有没有跑偏。
  2. 左侧栏——任务列表。按时间倒排(最耗时在最上面)。
  3. 右侧瀑布图——每个任务的开始 & 结束时间。重叠的条表示并行执行,空档就是等待或序列瓶颈。

举个例子:你看到 :app:compileJava 占了 30 秒,但 :lib:compileJava 只占了 5 秒,而且它俩没有重叠——说明你的项目模块依赖没解耦,任务在排队等 CPU。这就是经典的串行瓶颈。

4.4 真正读懂的三个关键指标

指标含义我遇到过的问题
配置阶段耗时 Gradle 解析脚本、构建 DSL 的时间 有一次,一个团队的项目用了 200 多个插件,配置阶段占了总时间 60%。我直接建议他们按需加载插件。
依赖解析耗时 下载仓库里 jar、aar 的时间 有个项目依赖卡在 mavenCentral 上,换到阿里云镜像后,直接快了 4 倍。
最大并行度 同一时刻最多跑几个任务 默认并行数等于 CPU 核心数。如果你看到瀑布图里只有一两条条,说明并行没跑满。

你第一次扫出来后,建议先截个图。然后对照这些指标,逐一排查。

4.5 构建扫描的架构关系

下面这张图能帮你理解构建扫描在整个 Gradle 构建流程里的位置:

构建扫描数据流架构 构建扫描数据流架构 Gradle 构建进程 构建数据采集引擎 (插件 + 探针) 构建扫描服务 (https://gradle.com) --scan 参数 JSON 数据 开发者浏览器 URL 网络瀑布图 构建完成后,数据通过 HTTPS 上传到 Gradle 服务器 浏览器打开 URL 后,服务器渲染出瀑布图给你看

从图里能看明白:你的 Gradle 进程运行结束后,采集引擎会把性能数据打成一个 JSON「快照」,上传到 Gradle 官方的构建扫描服务。然后你拿到 URL,浏览器从服务端获取数据,渲染成瀑布图。

整个过程对项目代码零侵入。这就是为什么我敢在任何项目上直接加 --scan——出了事也不怕。

4.6 避坑指南:第一次扫描常见问题

  • 链接打不开?——检查能不能访问 gradle.com。公司内网可能需要代理,在 gradle.properties 里配好 systemProp.http.proxyHost。
  • 构建扫描显示「运行中」一直不结束?——可能是 uploadInBackground = false 没设。临时用 --scan 不会遇到这问题。
  • 数据太乱,看不懂?——别急,到第五、六章我会带你一步步拆解瀑布图里的具体信号。
我曾经踩过的坑:有一次我在 CI 上加了永久扫描上传,结果构建扫描链接居然公开了项目里的依赖路径信息。后来我强制加上了 buildScan.publishAlwaysIf(condition) 只在 PR 构建时触发。切记,公开的扫描默认是公开的,如果需要保密,得配置私有仓库或 Gradle Enterprise。

好了,你现在可以立刻跑一次 ./gradlew :app:assembleDebug --scan,拿到你的第一个扫描链接。别怕,数据多翻几遍就熟了。