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_InitDatabaseApp_InitNetwork。这样在trace里一目了然。别用 test1aaa 这种名字——你一周后回来看,绝对想不起来这是什么。

注意事项:

  • beginSectionendSection 必须成对出现,否则会crash
  • 打点有轻微性能开销(约几微秒),不要在循环里打点
  • Release版本默认不开启Trace,需要配合 Trace.setAppTracingAllowed(true)

我曾经在一个项目中,用自定义打点把Application的onCreate拆成了12个阶段。然后配合Perfetto,精确找到了三个耗时大户:数据库初始化(320ms)、插件加载(280ms)、首屏布局(150ms)。没有这些打点,你根本不知道问题在哪。

3.5 知识体系总览

下面这张图,帮你理清这四种测量工具的关系:

启动时间测量工具体系 adb命令 am start -W Systrace sched/gfx/view Perfetto 新一代追踪器 自定义打点 Trace.beginSection 特性对比 特性 adb命令 Systrace Perfetto 精度 毫秒级 微秒级 微秒级 CPU调度 不支持 支持 支持 自定义打点 不支持 支持 支持 SQL分析 不支持 不支持 支持

3.6 我的建议

说了这么多,到底怎么选?

  • 快速验证:用adb命令,看个大概
  • 定位问题:用Perfetto(Android 10+)或Systrace(老设备)
  • 精确测量:在关键路径上加自定义打点

这三种工具不是互斥的,而是互补的。我自己的流程是:先用adb命令确认「确实慢了」,然后用Perfetto看「哪里慢了」,最后用自定义打点锁定「具体是哪一行代码慢了」。

嗯,工具就聊到这。下一章咱们开始动手——从Application的onCreate入手,看看那些「看起来无害」的初始化代码,到底偷走了多少时间。


公众号:蓝海资料掘金营,微信deep3321