帧率监控工具概览

说到SystemUI启动帧率监控,很多人第一反应就是“用Systrace啊”。说实话,我刚开始也这么想,直到被现实教育了一顿。不同场景下,工具的适用性天差地别。你以为Systrace能解决一切?嗯,它确实很强大,但抓一次要十几秒,还不一定能抓到启动那一瞬间的细节。那dumpsys呢?实时性强,但信息量又不够细。所以,我的经验是——要打组合拳。

这一章我把常用的几个工具捋一遍,说说它们的优劣,也讲讲我实际踩过的坑。你想想看,如果你在产线上遇到SystemUI开机掉帧,连用哪个工具都不知道,那不是白加班了吗?

1. dumpsys —— 命令行里的帧率仪表盘

dumpsys是Android自带的诊断工具,不用任何权限,adb敲一行就能用。我最常用的是 dumpsys gfxinfo 和 dumpsys SurfaceFlinger。这两兄弟能直接告诉你应用的帧率、渲染耗时、以及SurfaceFlinger的合成帧率。

举个例子,抓SystemUI的帧信息:

adb shell dumpsys gfxinfo com.android.systemium --framestats

加 --framestats 参数后,会输出最近128帧的详细数据,包括每个阶段的耗时(Input、Animation、Layout、Draw、Sync、...)。我过去排查一个SystemUI启动动画卡顿问题时,就是靠这行命令定位到Draw阶段超时,最终发现是某个自定义View的onDraw里做了大量Path计算。

避坑指南: 我曾经直接 dumpsys gfxinfo 不带参数,发现拿到的帧率不准。因为默认只输出最后几帧的汇总,而 --framestats 才能看到完整的时间线。另外,SystemUI进程名是 com.android.systemui,不是 systemui。每次测试前最好确认一下进程是否存活。

另一个利器是 dumpsys SurfaceFlinger --latency。它用来获取SurfaceFlinger合成每一帧的硬件时间戳,能反映真实掉帧:

adb shell dumpsys SurfaceFlinger --latency SurfaceView

这里的 SurfaceView 要替换成你关心的图层名称,比如SystemUI的导航栏或状态栏。如果输出的数据中,相邻时间戳间隔超过16.67ms,那表示丢帧了。说白了,这是最底层、最客观的帧率证据,比上层APK报告的帧率更可靠。

2. Systrace / Perfetto —— 时间线上的放大镜

Systrace(在Android 10以后被Perfetto取代)是另一个经典工具。它能抓取整个系统的时间线,包括CPU调度、Binder通信、SurfaceFlinger合成、应用渲染等。对于SystemUI启动场景,它的价值在于:你可以看到启动过程中每一帧到底卡在哪个阶段,以及那个阶段CPU在干什么。

我个人习惯用Perfetto来抓,因为它支持长时间抓取,而且UI界面更友好。抓取SystemUI启动帧率的典型命令:

adb shell perfetto -c - -o /data/misc/perfetto-traces/trace.perfetto-trace \
    --txt \
    --config '{
        "buffers": [{"size_kb": 32768}],
        "data_sources": [
            {"config": {"name": "linux.ftrace", "target_buffer": 0,
                "ftrace_config": {
                    "ftrace_events": ["sched/sched_switch",
                                     "power/cpu_frequency",
                                     "gfx/*",
                                     "mdss/*",
                                     "sync/*"]}}},
            {"config": {"name": "android.process_em_data", "target_buffer": 0}}
        ],
        "duration_ms": 5000
    }'

设置5秒,足够捕获SystemUI从冷启动到首帧的完整过程。抓完导到 ui.perfetto.dev 里打开,你会发现系统UI的渲染线程(RenderThread)和UI线程(main)的时间片清清楚楚。如果启动的时候有掉帧,你会看到帧渲染时长超过16ms,然后SurfaceFlinger那行就会显示“Jank”。

我曾经在一次项目里,明明App侧没发现掉帧,但用户就是觉得动画卡。用Perfetto一查,发现是SurfaceFlinger合成了两帧后,因为GPU空闲时间太长,导致第三帧被推迟了。嗯,其实问题出在硬件Overlay的分配上,根本不是SystemUI的锅。

小技巧: 如果只是快速验证启动帧率,可以用 atrace + systrace.py 旧工具。但遇到复杂问题,强烈建议上Perfetto,因为它的SQL分析引擎可以写查询统计帧耗时分布,比如 SELECT COUNT(*) FROM slice WHERE name LIKE '%Jank%'。

3. 其他工具补充:GPU渲染模式与FrameMetrics API

除了命令行和系统trace,还有两个轻量级工具值得提一下。

  • Profile GPU Rendering(GPU渲染模式):在开发者选项里打开“GPU渲染模式分析”,屏幕上会出现实时柱状图。每根柱子代表一帧,颜色区分不同阶段。如果柱子超过绿色横线(16.67ms线),就表示掉帧。我通常用它做快速初步判断,比如在SystemUI启动过程中,打开这个功能,再按Home键返回桌面,看看柱子是否持续飙高。但它只能显示当前应用的帧,不能记录历史,也不适合自动化测试。
  • FrameMetrics API:这是官方推荐给App开发者使用的代码级API。你可以通过 Window.OnFrameMetricsAvailableListener 拿到每帧的耗时统计。虽然SystemUI是系统应用,但同样可以注入。我曾在SystemUI的BaseStatusBar里注册这个监听器,把帧数据写到log里,再用logcat分析。好处是无痕、可集成到自动化测试,坏处是需要改代码。

说到改代码,就不得不提 FrameTimingMetrics 这个隐藏类。SystemUI里其实已经内置了一些帧率监控的代码,只是默认没打开。我建议在调试版本里打开 SysUIFrameMetrics 的开关,这样能拿到更详细的帧数据,包括渲染管道每一步的耗时。

4. 工具对比一览

我把这几个工具的核心参数整理成了表格,方便你按需选用:

工具 数据来源 精度 场景 是否需要改代码
dumpsys gfxinfo 应用侧渲染流水线 帧级(逐帧耗时) 离线分析、自动化测试 否
dumpsys SurfaceFlinger 合成器硬件时间戳 纳秒级 低层帧率确认、vsync偏移 否
Systrace / Perfetto 系统全局(CPU/GPU/合成) 微秒级 定位卡顿根因、调度分析 否(仅需抓trace)
Profile GPU Rendering 应用实时渲染柱子 帧级(粗略) 快速验证、直观观察 否(开发者选项开关)
FrameMetrics API 应用代码回调 帧级(精确上报) 集成到自动化、工程调试 是(需注册Listener)

5. 我的推荐组合

你问我在实际项目中怎么用?我不喜欢一套工具打天下。我的流程通常是:

  1. 第一步:用Profile GPU Rendering快速看一眼,确认是否有明显掉帧。如果有,进入下一步。
  2. 第二步:跑一遍Perfetto抓取SystemUI启动过程,时长5~10秒。然后在Perfetto UI里用“Jank”筛选器找出所有掉帧点,并定位到具体阶段(UI线程、RenderThread还是GPU)。
  3. 第三步:如果怀疑是SurfaceFlinger合成问题,再用 dumpsys SurfaceFlinger --latency 验证硬件时序。
  4. 第四步:如果是代码逻辑问题,比如启动时大量布局计算,我会临时在SystemUI代码里加FrameMetrics监听器,把每一帧的draw耗时输出到log,然后通过logcat对比不同版本的启动帧率。

这套方法我用了好几年,基本没有搞不定的启动帧率问题。你想想看,如果只依赖一个工具,很容易被片面的信息误导。只有把它们组合起来,才能像手术刀一样精准切开SystemUI启动那一刻的每一帧。

核心要记住: dumpsys给的是结果,Perfetto给的是过程。结果对不上过程的时候,一定是某个环节被忽略了。我遇到最坑的一次是Perfetto显示帧率正常,但用户反馈卡——后来发现是dumpsys SurfaceFlinger的时间戳因为GPU负载太高而偏移了。所以,交叉验证才是王道。
SystemUI启动帧率监控工具关系图 SystemUI启动帧率监控工具生态图 SystemUI 启动 dumpsys Systrace/Perfetto GPU渲染模式 FrameMetrics API 帧率统计 / 渲染流水线耗时 SurfaceFlinger时间戳 时间线火焰图 / 帧耗时分布 CPU/GPU同步细节 实时柱状图 是否超过16.67ms线 逐帧回调数据 可集成Statsd上报 使用场景推荐 快速验证 → GPU渲染模式 | 根因定位 → Perfetto | 自动化回归 → dumpsys + FrameMetrics API

这张图把整个工具生态串起来了。你从上往下看:SystemUI启动过程是靶子,四个工具从不同维度射击,命中输出各种数据,最终归拢到使用场景。我自己在实战中,经常是左边两个(dumpsys、Perfetto)用得最多,右边两个(GPU渲染模式、FrameMetrics API)作为辅助。记住,没有完美的工具,只有最合适的组合。