1. Choreographer概述:它是什么,为什么重要
我在刚做Android性能优化的时候,经常遇到一种困惑:明明代码写得挺流畅,用户滑动列表却感觉「一卡一卡的」。那时候我连Choreographer是什么都不知道,直到被线上反馈逼得去翻源码,才算真正入了门。
说白了,Choreographer就是Android框架里的「舞蹈指挥」。它负责协调屏幕刷新、输入事件、动画和View绘制的节奏。嗯,就像乐队里的指挥家——所有乐器必须跟着同一个节拍走,否则音乐就乱了。
1.1 Choreographer在渲染管线里的角色
Android的每一帧画面,从CPU计算到GPU绘制再到屏幕显示,都经过一条明确的管线。你想想看,要是没有Choreographer,每个模块想什么时候干活就什么时候干活,那画面撕裂、掉帧、延迟就全来了。
我用一张图来说明它的位置和职责。这是我自己整理的核心流程,项目中每次做帧率分析都会对照着看。
从图里能看到,Choreographer站在VSync信号和UI更新之间。每次屏幕刷新前,它都会收到一个硬件VSync,然后依次触发:输入处理 → 动画更新 → 布局遍历 → 绘制列表生成。最后SurfaceFlinger把各层合在一起交给GPU。
我在项目中遇到过一种典型问题:Choreographer在doFrame里耗时太长,导致后续的VSync被跳过,一卡就是好几帧。那时候我才意识到,这家伙的调度队列一旦堵塞,整个UI线程都得等。
1.2 为什么需要监控帧率
你可能觉得「帧率掉几帧没什么,用户看不出来」。我刚开始也这么想,直到有一次上线了一个新功能,灰度用户反馈「滑动时头像框在抖」——其实帧率只从60掉到45左右,但人眼对抖动的敏感度远高于对「延迟」的容忍度。
监控帧率有三个核心目的,我列个表方便你对照项目情况:
| 目的 | 具体价值 | 我的经验 |
|---|---|---|
| 量化流畅度 | 用FPS、帧耗时、掉帧数代替「感觉卡」 | 我们曾用Jank统计发现了某个ROM版本下的固定掉帧模式 |
| 定位瓶颈 | 区分是CPU、GPU还是渲染管线问题 | 监控帧率后发现某页面布局嵌套过深,measure耗时占帧时间40% |
| 回归防护 | 上线前对比帧率指标,防止性能劣化 | 我习惯在CI流水线里加帧率阈值任务,掉帧数超标就告警 |
核心指标速查
- FPS(帧每秒):理想值等于屏幕刷新率(60/90/120)。低于该值表示存在掉帧。
- 帧耗时:doFrame调用到绘制完成的时间。超过16.67ms(60Hz下)即为掉帧。
- 掉帧数:连续两帧之间的间隔超过1个VSync周期的次数。掉1帧用户可能不觉,掉3帧以上基本能感知「卡」。
1.3 Choreographer的工作原理简述
我在这里不深入源码,只说框架。Choreographer内部维护了一个回调队列,按三种类型排队:
- CALLBACK_INPUT —— 处理输入事件
- CALLBACK_ANIMATION —— 执行动画更新
- CALLBACK_TRAVERSAL —— 执行View的measure/layout/draw
它通过 postFrameCallback 或 postVsyncCallback 来注册下一帧要做的事。每次收到VSync后,它按顺序执行这些回调。如果某个回调耗时过长,后面回调就会被推迟,直接反映为掉帧。
⚠️ 注意
千万不要在主线程里做耗时操作,比如直接在主线程读写大文件、解压图片。我见过有人把网络请求结果解析放在 onDraw 里——那简直是在Choreographer的指挥棒上跳舞。掉帧掉到令人崩溃。
1.4 帧率监控工具有哪些
我用过很多工具,说说我目前最常用的三个:
- Profile GPU Rendering(GPU呈现模式分析):开发者选项里的柱状图,能直观看到每帧的耗时,不过颗粒度粗,只能看出「哪里拖后腿」。
- Choreographer.FrameCallback:自己打点,精度高,适合线上埋点。我们生产环境就用的这种方案,记录每帧开始时间和结束时间。
- Perfetto / Systrace:看全链路的神器,能看到VSync信号、Choreographer调度、SurfaceFlinger合成等所有过程。
💡 我的建议
如果你刚开始做帧率监控,先用开发者选项的「GPU呈现模式」观察页面,确认有问题后,再写代码埋点做量化统计。不要一上来就搞复杂工具,容易迷失在trace里。
嗯,以上是第一章节的核心内容。从下一篇开始,我会带你手写一个Choreographer帧率监听器,把掉帧数据采集到本地日志里。准备好了吗?