3、帧率测量(FPS):Choreographer原理、dumpsys gfxinfo、SurfaceFlinger如何获取帧率数据
帧率这玩意儿,说白了就是手机屏幕每秒能刷多少帧画面。60 fps 还是 120 fps,用户体验差得不是一星半点。我最早接触性能优化时,以为帧率就是掐个秒表数画面,后来才发现——Android 的帧率测量背后藏着一整套机制。
这一章,咱们就掰开揉碎聊聊:Choreographer 怎么驱动渲染?dumpsys gfxinfo 到底抓了什么数据?SurfaceFlinger 又凭什么能告诉你真实的掉帧情况?
3.1 Choreographer:Android 渲染的"心跳"
Choreographer 这个词,直译过来是"编舞者"。它在 Android 里的角色,就是协调所有跟渲染相关的操作。我个人习惯把它理解成渲染节奏的节拍器。
每次屏幕刷新前,硬件会发一个 VSync 信号。Choreographer 收到这个信号后,会依次触发三个回调:
- input 回调 —— 处理用户输入事件
- animation 回调 —— 执行动画帧
- traversal 回调 —— 触发 measure / layout / draw
这三个回调跑完了,一帧的渲染数据才算准备好。然后交给 RenderThread 去渲染,最后送到 SurfaceFlinger 合成。
adb shell dumpsys gfxinfo 能看到每个回调的耗时分布。
为什么会卡?说白了就是 Choreographer 的回调队列里,某个任务超时了。我记得有一次排查一个列表滑动卡顿,发现是某个 View 的 onDraw 里做了 Bitmap 解码,直接拖垮了 traversal 回调。
3.2 dumpsys gfxinfo:命令行里的帧率放大镜
这个工具是我日常用得最多的。你不需要装任何第三方工具,一行命令就能拿到帧率数据。
adb shell dumpsys gfxinfo <package_name>
它会输出一大堆数据,但最关键的是这两块:
- 帧时间直方图 (Frame timeline) —— 每一帧的渲染耗时,精确到毫秒
- 掉帧统计 (Jank frames) —— 超过 16ms 的帧会被标记为掉帧
举个例子,输出里你会看到类似这样的内容:
Window: com.example.myapp
Stats since: 320000000000ns ago
Total frames rendered: 1200
Janky frames: 45 (3.75%)
90th percentile: 18ms
95th percentile: 22ms
99th percentile: 32ms
这里 Janky frames 就是掉帧数,3.75% 意味着每 100 帧里有将近 4 帧是卡顿的。我个人习惯把这个比例控制在 1% 以内才算合格。
- 90th percentile < 16ms → 流畅
- 90th percentile 16~20ms → 轻度卡顿
- 90th percentile > 20ms → 明显卡顿,需要优化
3.3 SurfaceFlinger:系统级的帧率守门员
Choreographer 管的是应用层的渲染编排,但真正把各层合成并送显的是 SurfaceFlinger。它是系统服务,运行在独立的进程中。
你可以通过下面的命令查看 SurfaceFlinger 的帧率统计:
adb shell dumpsys SurfaceFlinger --latency
这个命令会输出每一帧的 时间戳数组,包括:
- app 时间 —— 应用开始渲染的时间
- composition 时间 —— SurfaceFlinger 开始合成的时间
- 送显时间 —— 实际显示到屏幕的时间
通过对比这些时间戳,你能精确算出每一帧从诞生到显示花了多久。这是最接近"用户真实感受"的帧率数据。
SurfaceFlinger 还提供了另一个有用的命令:
adb shell dumpsys SurfaceFlinger --list
它会列出当前所有的 Layer(图层),以及每个图层的帧率、分辨率、格式等信息。你可以在线上快速确认是不是某个图层拖慢了整体帧率。
3.4 三种方法对比:什么时候用哪个?
| 测量方法 | 数据来源 | 精确度 | 适用场景 |
|---|---|---|---|
| Choreographer 回调 | 应用内部回调耗时 | ★★★ 中 | 开发阶段定位卡顿代码 |
| dumpsys gfxinfo | 应用帧时间直方图 | ★★★★ 高 | 线下 / 自动化性能测试 |
| SurfaceFlinger latency | 系统合成时间戳 | ★★★★★ 最高 | 线上 + 线下,排查合成瓶颈 |
你想想看,如果只是开发时自测,用 Choreographer 回调就够了。但要出性能报告给老板看,我建议用 dumpsys gfxinfo 拿掉帧率。要是线上用户反馈卡顿,必须上 SurfaceFlinger —— 因为只有它能告诉你系统层到底发生了什么。
- 日常开发:Choreographer 回调 +
adb logcat看帧耗时 - CI 自动化:dumpsys gfxinfo 采集掉帧率
- 线上疑难:SurfaceFlinger latency 全链路追踪
嗯,帧率测量这块,说到底就是搞清楚数据从哪儿来、在哪儿卡、怎么度量。Choreographer 告诉你应用层有没有偷懒,dumpsys gfxinfo 把帧耗时掰开了给你看,SurfaceFlinger 则站在系统层给你最终的"判决"。三个层面都吃透了,帧率问题基本逃不过你的眼睛。