26. 启动优化工具链:Perfetto深度使用、SimplePerf、Firebase Performance
做启动优化,最怕什么?
怕你改了一堆代码,自我感觉良好,结果一测——更慢了。
所以,工具链就是你的眼睛。没有它,你就是在黑灯瞎火里修车。今天我把压箱底的三件套拿出来:Perfetto、SimplePerf、Firebase Performance。这三样东西,我几乎每天都要跟它们打交道。
Perfetto:启动优化的“显微镜”
Perfetto 是 Google 官方的性能追踪工具。说白了,它能把你手机从开机到应用启动完成,每一毫秒在干什么都记录下来。
我个人习惯:先跑一次 Perfetto,再看火焰图。为什么?因为 Perfetto 能告诉你“时间花在哪了”,而火焰图告诉你“CPU 在忙什么”。两者缺一不可。
Perfetto 的三种启动模式
| 模式 | 命令 | 适用场景 |
|---|---|---|
| 轻量模式 | perfetto --txt -c /path/to/config -o /data/misc/perfetto-traces/trace.perfetto-trace |
快速定位主线程卡顿 |
| 详细模式 | perfetto --txt -c /path/to/config -o /data/misc/perfetto-traces/trace.perfetto-trace --full |
分析 Binder 调用、锁竞争 |
| 持续追踪 | 通过 Perfetto UI 的“Record new trace”按钮 | 复现偶现问题 |
嗯,这里要注意:轻量模式就够了,别一上来就开全量。我见过有人开全量追踪,结果手机直接卡死,重启后 trace 文件还损坏了。
我常用的 Perfetto 配置
buffers: {
size_kb: 522240
fill_policy: DISCARD
}
data_sources: {
config {
name: "android.surfaceflinger.frametimeline"
}
}
data_sources: {
config {
name: "android.scheduling"
}
}
data_sources: {
config {
name: "linux.syscalls"
}
}
duration_ms: 10000
这个配置只抓 10 秒,重点看 SurfaceFlinger 帧时间线和系统调用。我曾经用这个配置抓到一个诡异问题:启动时有个线程在疯狂做 futex 系统调用,导致主线程被频繁挂起。查了三天,最后发现是某个第三方 SDK 在后台轮询。
SimplePerf:CPU 火焰图的利器
Perfetto 告诉你“时间线”,SimplePerf 告诉你“CPU 在跑什么代码”。
为什么需要它?因为有时候启动慢,不是主线程卡,而是某个后台线程把 CPU 吃满了。你想想看,8 核 CPU,如果 4 个核都在跑垃圾回收,主线程能快吗?
基本用法
# 采样 10 秒,生成火焰图数据
simpleperf record -p $(pidof com.example.app) -e cpu-clock -f 1000 --duration 10 -o perf.data
# 生成报告
simpleperf report -i perf.data --sort comm,dso,symbol -o report.txt
# 生成火焰图
simpleperf report_html -i perf.data -o flamegraph.html
避坑指南:我曾经在某个项目上直接用 simpleperf record 不加 -f 参数,结果采样频率太低,火焰图里全是“unknown”。后来才意识到,默认采样频率只有 100Hz,对于启动这种毫秒级事件,根本不够看。建议至少 1000Hz。
火焰图怎么看?
记住三个原则:
- 看宽度:哪个函数占的 CPU 时间最长,哪个就是嫌疑犯
- 看颜色:红色通常表示内核态,蓝色表示用户态
- 看堆叠:如果某个调用链特别深,说明可能有过度封装
我见过最离谱的一次:某个应用的启动火焰图里,android.os.Handler.dispatchMessage 占了 40% 的 CPU 时间。点进去一看,好家伙,一个 Handler 里塞了 200 多个 Runnable,每个都做了一点点 IO 操作。这就是典型的“微任务堆积”。
Firebase Performance:线上监控的“雷达”
Perfetto 和 SimplePerf 都是本地工具。但线上用户的环境千奇百怪,你不可能让每个用户都连 ADB 抓 trace。
Firebase Performance 就是干这个的。它能自动采集启动时间、网络请求、屏幕渲染等指标,然后上报到后台。
核心指标
| 指标 | 含义 | 我建议的阈值 |
|---|---|---|
| App Start Time | 从点击图标到首帧渲染 | < 1.5s (P50), < 3s (P95) |
| Cold Start | 冷启动时间 | < 2s |
| Warm Start | 热启动时间 | < 1s |
| HTTP Response Time | 网络请求响应时间 | < 200ms |
自定义追踪
Firebase Performance 还支持自定义 Trace。我通常会在关键路径上加:
Trace trace = FirebasePerformance.getInstance().newTrace("cold_start_init");
trace.start();
// 初始化各个模块
initSDK();
initDatabase();
initUI();
trace.stop();
这样你就能看到每个模块花了多少时间。我记得有一次,线上数据显示 initSDK() 在 P95 上花了 800ms,而本地测试只有 200ms。后来发现是某个 SDK 在弱网环境下会做超时重试,导致启动变慢。
核心观点:本地工具(Perfetto + SimplePerf)解决“能不能优化”的问题,线上工具(Firebase Performance)解决“优化有没有效果”的问题。两者缺一不可。
三件套的配合使用
我一般的工作流是这样的:
- 线上报警:Firebase Performance 发现启动时间异常
- 本地复现:用 Perfetto 抓 trace,看时间线
- 定位热点:用 SimplePerf 生成火焰图,找到 CPU 热点
- 优化代码:改完后,再用 Perfetto 验证
- 上线观察:Firebase Performance 确认指标下降
这个闭环,我用了好几年,屡试不爽。
小技巧:Perfetto 的 trace 文件通常很大(几百 MB),建议用 --compress 参数压缩后再分享。我一般会压缩成 .gz 格式,能缩小 10 倍以上。
警告:不要在用户手机上开启 Perfetto 或 SimplePerf 的持续追踪。这两个工具会显著增加功耗和 CPU 负载。线上监控请只用 Firebase Performance 这类轻量级方案。
知识体系总览
下面这张图,是我对启动优化工具链的理解。你可以把它当作一个“作战地图”:
这张图里,Perfetto 和 SimplePerf 是本地工具,Firebase Performance 是线上工具。它们不是替代关系,而是互补关系。你想想看,没有本地工具,你连问题在哪都不知道;没有线上工具,你改了代码也不知道有没有效果。
好了,工具链就讲到这里。下一章我们聊聊具体怎么用这些工具去优化启动流程——从 Application 的 onCreate 开始,一步步拆解。
公众号:蓝海资料掘金营,微信deep3321