3. 启动时间测量:adb命令、Systrace、Perfetto、自定义打点
做性能优化,第一件事是什么?
不是改代码,而是先搞清楚——时间到底花在哪了。
我见过太多人上来就怀疑「是不是SurfaceFlinger太慢」「是不是HAL层有bug」,结果折腾半天,发现是自己在主线程里做了个文件读写。嗯,这种坑我也踩过。
所以这一章,咱们把测量工具聊透。从最基础的adb命令,到Systrace、Perfetto,再到自定义打点。你把这些吃透了,启动优化的地基才算打牢。
3.1 adb命令:最直接的测量方式
先说最简单的。你不需要任何工具,一台手机、一根数据线就够了。
核心命令:
adb shell am start -W -n package/activity
这个命令会输出三个关键时间:
- ThisTime:当前Activity的启动耗时
- TotalTime:整个应用进程的启动耗时(包含Application onCreate)
- WaitTime:包含系统调度、AMS处理的总时间
我个人习惯用 TotalTime 作为基准。为什么?因为它包含了Application的初始化,而这往往是启动优化的重灾区。
小技巧: 多测几次取中位数。冷启动受系统调度影响很大,单次数据不可信。我一般测10次,去掉最高最低,取中间5次的平均值。
但adb命令有个局限——它只能告诉你「花了多少毫秒」,却没法告诉你「这500ms里CPU在干什么」。想知道细节?得上Systrace。
3.2 Systrace:老牌工具,依然能打
Systrace是Google在Android 4.1引入的工具。说实话,它有点老了,但在某些场景下依然好用。
怎么用?
python systrace.py -t 10 -o trace.html sched gfx view am wm
这里 -t 10 表示抓取10秒,sched gfx view am wm 是你要跟踪的标签。
抓完之后,你会得到一个HTML文件。用Chrome打开,就能看到一条时间轴。上面有CPU调度、线程状态、UI渲染……
看什么?
- 看主线程有没有长时间卡住(比如做I/O、加锁等待)
- 看SurfaceFlinger的合成节奏是否稳定
- 看Binder调用是否频繁
注意: Systrace在Android 10之后被标记为deprecated。Google推荐用Perfetto替代。但如果你还在维护Android 9及以下的设备,Systrace依然是首选。
我记得有一次,客户反馈某款手机启动特别慢。我用Systrace一看,发现主线程在启动过程中调了三次 PackageManager.queryIntentActivities(),每次耗时80ms。这就是典型的「不经意间埋的雷」。
3.3 Perfetto:新一代性能追踪利器
Perfetto是Systrace的继任者。它更强大,也更复杂。
抓取命令:
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq gfx view am wm
抓完后pull到本地,用 ui.perfetto.dev 打开。
Perfetto比Systrace强在哪?
- 支持更细粒度的CPU频率跟踪
- 可以同时跟踪多个进程
- 支持SQL查询分析(这个太强了)
- 内存占用更低,适合长时间抓取
我个人最常用的是Perfetto的「SQL分析」功能。比如我想知道启动过程中所有Binder调用的耗时分布,直接写一条SQL就能查出来:
SELECT name, dur FROM slice WHERE name GLOB '*binder*' ORDER BY dur DESC LIMIT 10
这比在时间轴上肉眼找快多了。
避坑指南: 我曾经在抓取Perfetto trace时,忘记设置buffer大小。结果启动还没跑完,buffer就满了,关键数据全丢了。建议设置 --buffer-size 64mb,保险一点。
3.4 自定义打点:Trace.beginSection
adb命令和系统工具能帮你看到「系统层面」的耗时。但如果你想精确测量「自己代码」里的某个函数,就得靠自定义打点了。
Android提供了 Trace 类:
import android.os.Trace;
// 开始打点
Trace.beginSection("MyApp.onCreate");
// 你的初始化代码...
initDatabase();
initNetwork();
initUI();
// 结束打点
Trace.endSection();
打点之后,你在Systrace或Perfetto的时间轴上,就能看到 MyApp.onCreate 这个区间。鼠标悬停上去,会显示精确的起止时间和耗时。
命名规范: 我建议用「模块_功能」的格式,比如 App_InitDatabase、App_InitNetwork。这样在trace里一目了然。别用 test1、aaa 这种名字——你一周后回来看,绝对想不起来这是什么。
注意事项:
beginSection和endSection必须成对出现,否则会crash- 打点有轻微性能开销(约几微秒),不要在循环里打点
- Release版本默认不开启Trace,需要配合
Trace.setAppTracingAllowed(true)
我曾经在一个项目中,用自定义打点把Application的onCreate拆成了12个阶段。然后配合Perfetto,精确找到了三个耗时大户:数据库初始化(320ms)、插件加载(280ms)、首屏布局(150ms)。没有这些打点,你根本不知道问题在哪。
3.5 知识体系总览
下面这张图,帮你理清这四种测量工具的关系:
3.6 我的建议
说了这么多,到底怎么选?
- 快速验证:用adb命令,看个大概
- 定位问题:用Perfetto(Android 10+)或Systrace(老设备)
- 精确测量:在关键路径上加自定义打点
这三种工具不是互斥的,而是互补的。我自己的流程是:先用adb命令确认「确实慢了」,然后用Perfetto看「哪里慢了」,最后用自定义打点锁定「具体是哪一行代码慢了」。
嗯,工具就聊到这。下一章咱们开始动手——从Application的onCreate入手,看看那些「看起来无害」的初始化代码,到底偷走了多少时间。
公众号:蓝海资料掘金营,微信deep3321