1. Choreographer概述:它是什么,为什么重要

我在刚做Android性能优化的时候,经常遇到一种困惑:明明代码写得挺流畅,用户滑动列表却感觉「一卡一卡的」。那时候我连Choreographer是什么都不知道,直到被线上反馈逼得去翻源码,才算真正入了门。

说白了,Choreographer就是Android框架里的「舞蹈指挥」。它负责协调屏幕刷新、输入事件、动画和View绘制的节奏。嗯,就像乐队里的指挥家——所有乐器必须跟着同一个节拍走,否则音乐就乱了。

1.1 Choreographer在渲染管线里的角色

Android的每一帧画面,从CPU计算到GPU绘制再到屏幕显示,都经过一条明确的管线。你想想看,要是没有Choreographer,每个模块想什么时候干活就什么时候干活,那画面撕裂、掉帧、延迟就全来了。

我用一张图来说明它的位置和职责。这是我自己整理的核心流程,项目中每次做帧率分析都会对照着看。

Choreographer在Android渲染管线中的角色 Choreographer在Android渲染管线中的角色 输入事件(触摸/按键) InputReader → InputDispatcher Choreographer(舞蹈指挥) 接收VSync信号 → 调度回调 动画(Animation) 更新属性、插值 遍历(Traversal) measure / layout 绘制(Draw) DisplayList 生成 SurfaceFlinger 合成 + 提交到GPU 屏幕刷新 60Hz / 90Hz / 120Hz VSync 信号 📌 核心职责 1. 接收HW_VSYNC 2. 分发CALLBACK_INPUT 3. 分发CALLBACK_ANIMATION 4. 分发CALLBACK_TRAVERSAL 5. 触发doFrame()回调

从图里能看到,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内部维护了一个回调队列,按三种类型排队:

  1. CALLBACK_INPUT —— 处理输入事件
  2. CALLBACK_ANIMATION —— 执行动画更新
  3. 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帧率监听器,把掉帧数据采集到本地日志里。准备好了吗?