Android 屏幕刷新机制 Choreographer 原理分析

  1. Android 平台提供两种信号,一种是硬件信号,另一种是软件信号,由 SurfaceFlinger 进程的一个线程定时发出,硬件信号由硬件发出;
  2. App 进程若要通过 gpu 实现图像绘制,需要在接收到 Vsync 信号的条件下进行。因此,App 进程访问 SurfaceFlinger 进程获取这个信号,再进行 gpu 绘制;
  3. Android4.1 之后增加了 Choreographer 机制,用于同 Vsync 机制配合,统一动画、输入和绘制时机;
  4. Choreographer 就是负责获取 Vsync 同步信号并控制 App 主线程完成图像绘制的类;

Choreographer 源码分析

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
Coordinates the timing of animations, input and drawing.
The choreographer receives timing pulses (such as vertical synchronization)
from the display subsystem then schedules work
to occur as part of rendering the next display frame.
*/
public final class Choreographer {
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;

private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//使用数组保存五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
}
  • 根据当前线程的 looper 创建 Handler 对象;
  • 变量 USE_VSYNC 用于表示系统是否使用了 Vsync 同步机制,该值是通过读取系统属性 debug.choreographer.vsync 来获取的。4.1 以上默认是 true;
  • 创建一个 FrameDisplayEventReceiver 对象用于接收 Vsync 事件;
  • 使用 CallbackQueue(单链表结构)保存相同类型的任务,所有类型的任务保存在 CallbackQueue 数组中;

Choreographer 对象的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class Choreographer {
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};

public static Choreographer getInstance() {
return sThreadInstance.get();
}
}
  • Choreographer 对象通过 ThreadLocal 保存在每个线程中;
  • Choreographer 对象初始化时需要提供 Looper 对象,构造 Handler 发送消息;

Callback 类型

1
2
3
4
5
6
7
8
9
10
11
// Choreographer.java
//输入事件,首先执行
public static final int CALLBACK_INPUT = 0;
//动画,第二执行
public static final int CALLBACK_ANIMATION = 1;
//插入更新的动画,第三执行
public static final int CALLBACK_INSETS_ANIMATION = 2;
//绘制 View,第四执行
public static final int CALLBACK_TRAVERSAL = 3;
//提交,最后执行。遍历完成的提交操作,用来修正动画启动时间
public static final int CALLBACK_COMMIT = 4;
  • 五种类型任务对应存入对应的 CallbackQueue 中;
  • 每当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的任务,然后是 ANIMATION 类型,最后才是 TRAVERSAL 类型;

FrameHandler 处理的消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Choreographer.java
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}

FrameHandler 处理了如下三种消息:

  • MSG_DO_FRAME:处理注册在 Choreographer 的 Runnable;
  • MSG_DO_SCHEDULE_VSYNC:直接请求下一帧的 VSync 信号;
  • MSG_DO_SCHEDULE_CALLBACK:处理延时消息;

Choreographer 处理任务流程

choreographer-callbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Provides a low-level mechanism for an application to receive display events
* such as vertical sync.
*
* The display event receive is NOT thread safe. Moreover, its methods must only
* be called on the Looper thread to which it is attached.
*/
public abstract class DisplayEventReceiver {
public void scheduleVsync() {
nativeScheduleVsync(mReceiverPtr);
}

// Called from native code.
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}

// Called when a vertical sync pulse is received.
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
}
}

DisplayEventReceiver 用来接收从底层发来的 Vsync 信号,并回调 onVsync 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Choreographer.java
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
// timestampNanos其实是本次vsync产生的时间,从服务端发过来
// 该消息的callback为当前对象
Message msg = Message.obtain(mHandler, this);
// 前面添加了同步屏障,同步消息不会被执行
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

@Override
public void run() {
doFrame(mTimestampNanos, mFrame);
}

void doFrame(long frameTimeNanos, int frame) {
if (!mFrameScheduled) {
return; // no work to do
}

if (frameTimeNanos < mLastFrameTimeNanos) {
// 这种情况一般是生成vsync的机制出现了问题,那就再申请一次
scheduleVsyncLocked();
return;
}

// intendedFrameTimeNanos是本来要绘制的时间戳,frameTimeNanos是真正的,可以在渲染工具中标识延迟VSYNC多少
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
// 移除mFrameScheduled判断,说明处理开始了
mFrameScheduled = false;

doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}

void doCallbacks(int callbackType, long frameTimeNanos) {
// 获取当前类型 Callback 链表的首部节点
CallbackRecord callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
try {
// 遍历链表,执行任务。比如执行 ViewRootImpl#TraversalRunnable 任务。
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
// 遍历链表,重置 Callback
}
}
}
  1. FrameDisplayEventReceiver 继承 DisplayEventReceiver 并实现了 Runnable,表明它接收到 Vsync 信号后会执行任务;
  2. scheduleVsync() 方法通过 native 方法 nativeScheduleVsync() 向 SurfaceFlinger 服务注册,即在下一次脉冲接收后会调用 DisplayEventReceiver 的 dispatchVsync() 方法;
  3. 底层向应用层发送 VSYNC 信号,java 层通过 dispatchVsync() 接收,最后回调在 FrameDisplayEventReceiver#onVsync();

处理 View 绘制任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
//获取Choreographer实例。useSfChoreographer 默认 false。
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}

void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 开始测量,布局和绘制流程
performTraversals();
}
}

void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 同步屏障的作用是可以拦截 Looper 对同步消息的获取和分发。
// 加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}

void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}

public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}

VSync 机制

Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,VSync 是 Vertical Synchronization(垂直同步)的缩写。

VSync
VSync

  1. 16ms 内只会申请一次垂直同步信号;
  2. VSync 信号由 SurfaceFlinger 实现并定时发送。

总结

  1. Choreographer 的职责是统一处理输入、绘制、动画和提交任务;
  2. 主线程通过 Choreographer 把不同类型的 Callback 添加到单链表中。时间到后,根据 Callback 优先级遍历各个单链表执行任务;

参考

[1] 屏幕刷新机制 Choreographer 原理分析
[2] Android 屏幕刷新机制解析
[3] Android 屏幕刷新机制与 Choreographer 全面解读