网络瀑布图概念介绍
说实话,刚接触 AGP 构建优化的人,听到「网络瀑布图」第一反应可能是 Chrome 开发者工具。没错,原理上确实有共通之处——都是把一串串请求按时间轴摊开,让你一眼看出「谁在拖后腿」。只不过在 Android 构建场景里,瀑布图里的「请求」变成了 Gradle Task、资源编译、Dex 处理等等。
我最早在 AGP 3.x 时代就试过用 --profile 看构建耗时,但那时候图表太粗糙。直到 AGP 7.0+ 内置了更细粒度的瀑布视图(Build Analyzer 里面的 Network Waterfall),我才真正开始用它揪出那些藏在「并行任务」背后的顺序依赖瓶颈。
什么是网络瀑布图
网络瀑布图本质上是一个时序-任务关系图。横轴是时间(从 0 开始到构建结束),纵轴是各个任务或网络请求,每个条形的长度代表该任务的耗时,条形的左右位置代表它的开始和结束时刻。
举个实际场景:你在 Build Analyzer 里打开一个 AGP 7.4 项目,会看到类似这样的视图:
任务名称 时间轴 (ms)
:app:preBuild ████ 200
:app:mergeDebugResources ████████████████ 800
:app:processDebugManifest ██████ 300
:app:compileDebugKotlin ████████████████████████████ 1200
:app:dexBuilderDebug ██████████████ 700
这个图能清晰告诉你:哪个任务占的时间最长、哪些任务是串行等待的、哪些任务其实可以并行但被过晚调度了。
关键洞察:
- 条形越宽 → 该任务耗时越长(可能是瓶颈)
- 条形之间出现「水平间隙」→ CPU 空闲或 I/O 等待
- 条形重叠 → 并行任务(但注意:并行不是越多越好)
为什么 AGP 升级后特别需要看瀑布图
AGP 每次大版本升级,都会调整 Task 的依赖图结构。比如 AGP 7.0 引入了构建配置缓存、AGP 8.0 优化了资源处理管线。但这些改动可能导致某些原本并行的任务变成串行,或者新增了一些隐藏的依赖。
我记得有一次升级到 AGP 8.1 后,团队反馈增量构建反而变慢了。打开瀑布图一看,发现 :app:compileDebugKotlin 必须等 :app:processDebugResources 结束才能开始——这是 AGP 新加的一个硬依赖。我们说白了一点:如果不看瀑布图,你根本猜不到是哪个版本改动引入的依赖链变化。
避坑指南:我曾经在 AGP 7.2 → 7.4 升级后,发现 dexBuilder 任务被压到了所有编译任务之后,导致并行度下降。排查后发现是因为我把 android.enableR8 = true 和 minifyEnabled = true 同时打开了,AGP 默认强制串行处理混淆与 Dex。后来用瀑布图确认后,调整了混淆配置,构建时间降了 30%。
瀑布图中的关键指标
看瀑布图不能光看「谁最长」。我一般会盯着四个维度:
| 指标 | 含义 | 注意点 |
|---|---|---|
| 总构建时间 | 最右侧任务结束时间 | 这是最终结果,但别只看这个 |
| 关键路径长度 | 从起点到终点最长的一条依赖链 | 缩短关键路径上的任务才能加速整个构建 |
| 并行度 | 同一时刻正在运行的任务数 | 理想状态:CPU 核心数附近。过高会导致线程切换开销,过低说明串行瓶颈多 |
| 空闲间隙 | 两个任务之间没有任务运行的时间窗口 | 通常是因为 I/O 等待或依赖等待,需要排查 |
你想想看,如果关键路径上有个任务占了 40% 的总时间,你优化其他任务可能只带来 10% 的提升,但把这个任务砍一半,整个构建就能快 20%。这就是瀑布图帮你定位「杠杆点」的价值。
常见的瓶颈模式
我总结了几种在 AGP 升级后频繁出现的瀑布图特征:
-
长尾串行任务:某个任务结束后,后面跟着一大串单线程任务。比如
compileKotlin结束后,dexBuilder才开始,而dexBuilder又只能单线程跑。这种情况多见于开启 R8 或混淆后。 -
资源处理堵车:
mergeResources或processResources任务条极宽,而且它后面所有的任务都在等它。常见于项目中有大量 PNG、AAPT2 处理缓慢的场景。 -
并行度突然下降:原本有 8 个任务并行跑,突然变成 2 个。可能是因为 Gralde daemon 的并行线程数配置不够,或者某个自定义 Task 强制了
mustRunAfter。 -
任务依赖异常:你发现一个完全无关的 task(比如
:lib:generateJson)居然出现在:app:compileDebugKotlin前面的关键路径上。这通常是因为插件之间隐式依赖导致的,需要用--dry-run排查。
警告:不要只看「增量构建」的瀑布图。我建议同时对比全量构建和增量构建的两张瀑布图——增量构建中某些任务可能因为缓存命中而消失,但全量构建的依赖图才是真正的「骨架」。有一次我只看增量图,发现没有 mergeResources 所以误以为资源处理不慢,结果全量构建时那块占了 900ms。
用一张图说清瀑布图的结构
下面这张 SVG 展示了瀑布图的核心要素。嗯,我自己画的时候参考了 Build Analyzer 的布局——把时间轴、任务条形、依赖箭头和性能指标都放在一起。看懂这张图,你就能读懂 Gradle 构建的任何瀑布图了。
这张图里,红色任务 compileDebugKotlin 就是关键路径上的瓶颈——它占据了最长的连续时间,而且后面的 dexBuilder 只能等它结束。绿色和紫色任务并行跑,说明资源处理和清单解析可以同时进行。但注意到 preBuild 结束后,并没有立即开始资源任务,这里有一段几十毫秒的空隙(黄色框标注),可能是 Gradle 调度开销或者 I/O 等待。
怎么在 AGP 中获取这张图
其实很简单。从 AGP 7.0 开始,Build Analyzer 工具窗口就会自动生成这个瀑布图。你只需要:
- 执行一次构建(全量或增量都行)
- 在 Android Studio 右下角点击 Build Analyzer 按钮
- 选择「Network Waterfall」选项卡
如果你用 Gradle 命令行,也可以加 --profile 参数生成 HTML 报告,里面同样包含类似的瀑布图。我个人习惯两者都看:Studio 的交互式图可以鼠标悬停看详情,命令行报告更便于存档对比。
小技巧:第一次看瀑布图时,先不要管细节,直接找最长的连续彩色条形,那个大概率是瓶颈所在。然后看它前面的任务有没有「缝隙」——如果有,说明前面的任务没有充分利用好 CPU,可以考虑调整 org.gradle.parallel 或 org.gradle.caching 配置。