简介
Android 从 4.1 开始引入 Choreographer,它可以看作是整个控件系统的「心脏」,它驱动着动画和 UI 绘制等,作用非常重要,我们今天来学习一下。
官网对其的介绍是:
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.
翻译过来就是:choreographer 从显示子系统接收定时脉冲(比如垂直同步信号),然后在下一帧渲染时控制执行这些操作。
话不多说,我们直接来看源码。
构造函数
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
这里主要做一些初始化操作:
- 可以看到它需要一个 Looper,所以每个线程对应一个 Choreographer。
- 用对应的 Looper 创建了一个 Handler 用来做事件处理。
- 初始化 FrameDisplayEventReceiver ,FrameDisplayEventReceiver 用来接收垂直同步信号。
- 初始化 mLastFrameTimeNanos 用来记录上一帧事件,mFrameIntervalNanos 两帧之间的间隔时间,一般手机上为 1/60 s。
- 初始化 CallbackQueue 将会在下一帧渲染时找到合适的 callback 回掉。
成员
private final Object mLock = new Object();
private final Looper mLooper;
private final FrameHandler mHandler;
// The display event receiver can only be accessed by the looper thread to which
// it is attached. We take care to ensure that we post message to the looper
// if appropriate when interacting with the display event receiver.
private final FrameDisplayEventReceiver mDisplayEventReceiver;
//用来记录 callback 同时它也是一个链表,它持有下一个 callback 的引用。
private CallbackRecord mCallbackPool;
// callback 队列
private final CallbackQueue[] mCallbackQueues;
//
private boolean mFrameScheduled;
private boolean mCallbacksRunning;
//上一帧时间
private long mLastFrameTimeNanos;
//每两帧之间的间隔
private long mFrameIntervalNanos;
private boolean mDebugPrintNextFrameTimeDelta;
其中有一个 FrameHandler 和 FrameDisplayEventReceiver 下面看一下它们俩的源码:
FrameHandler
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;
}
}
}
比较简单,它只做三件事:
- MSG_DO_FRAME 处理下一帧的渲染
- MSG_DO_SCHEDULE_VSYNC 请求垂直同步信号
- MSG_DO_SCHEDULE_CALLBACK 执行 callback
MSG_DO_SCHEDULE_VSYNC 之前这里一直认为是执行垂直同步,然后很多地方就说不通。如果是请求垂直同步信号,确实更能说的通,而且整个逻辑清晰了很多。
FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
//暂时不支持第二块屏幕,如果当前屏幕不是主屏,重新请求信号
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ "this case yet. Choreographer needs a way to explicitly request "
+ "vsync for a specific display to ensure it doesn't lose track "
+ "of its scheduled vsync.");
scheduleVsync();
return;
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
FrameDisplayEventReceiver 继承自 DisplayEventReceiver,主要用来接收垂直同步信号。当有垂直同步信号到来时,发送一个信息到 FrameHandler ,这个消息的回掉指向的是自己。当这个消息触发的时候,自己的 run() 方法将会被执行。
不过中间的一段英文注释我没看懂。
上面主要是接收垂直同步信号并处理,那么怎么请求垂直同步信号?
Choreographer#postCallback
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
Choreographer#postCallbackDelayed
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
Choreographer#postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//添加一个指定类型的 callback 到 CallbackQueue
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
// 如果请求的时间小于等于当前时间,立即执行 scheduleFrameLocked
// 否则发送一个延时到 handler
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
注意上面做了一个时间判断,不过不是立即执行的只是通过 Handler 做了一个延时处理,最后都是走的 scheduleFrameLocked。
Choreographer#scheduleFrameLocked
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
// 如果当前线程是主线程,立即请求垂直同步事件
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
//当前线程不是主线程,发送消息到 Handler,注意这个消息是添加到队列最前面
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
// 不使用垂直同步的情况下,直接通过 handler 机制执行下一帧
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
Choreographer#scheduleVsyncLocked
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
DisplayEventReceiver#scheduleVsync
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
这里最终调用了 native 方法,不过看其注释应该是请求垂直同步事件。 请求垂直同步事件的流程已经走完,当垂直同步事件来的时候是怎么执行的? 下面看一下执行流程。
Choreographer#doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
// 是否有 callback 需要执行
// postCallBack 的时候置为 true
// 执行 frame 时置为 false
if (!mFrameScheduled) {
return; // no work to do
}
// 打印跳帧时间
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
// 垂直同步信号时间
long intendedFrameTimeNanos = frameTimeNanos;
//当前时间
startNanos = System.nanoTime();
//时间差
final long jitterNanos = startNanos - frameTimeNanos;
//时间差大于两帧间隔说明可能跳帧
if (jitterNanos >= mFrameIntervalNanos) {
//跳过的帧数
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//跳帧超过一定数目,打印警告信息
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
//修正偏差
frameTimeNanos = startNanos - lastFrameOffset;
}
//当前帧的时间小于上一帧的时间,这一帧将不会被执行,并且要重新请求垂直同步信号
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
scheduleVsyncLocked();
return;
}
//记录当前frame信息
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
//记录上一次frame开始时间
mLastFrameTimeNanos = frameTimeNanos;
}
try {
//执行相关 callback
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
doCallbacks 方法暂时不看了,至此请求垂直同步信号,接收信号并处理同步都已经看完。下面是流程图: