Choreographer 全解析
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
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;
}
}
}
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
在 ViewRootImpl 中调用了 postCallback 方法之后,可以看到通过 addCallbackLocked 方法,添加了一条 CallbackRecord 数据,其中 action 就是对应之前 ViewRootImpl 的 mTraversalRunnable。
然后判断设定的时间是否在当前时间之后,也就是有没有延迟,如果有延迟就发送延迟消息消息 MSG_DO_SCHEDULE_CALLBACK 到 Handler 所在线程,并最终执行到 scheduleFrameLocked 方法。如果没有延迟,则直接执行 scheduleFrameLocked。
scheduleFrameLocked(准备申请 VSYNC 信号)
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
//是否运行在主线程
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
//通过 Handler 切换线程
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
//计算下一帧的时间
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
该方法中,首先判断了是否开启了 VSYNC(上节说过 Android4.1 之后默认开启 VSYNC),如果开启了,判断在不在主线程,如果是主线程就运行 scheduleVsyncLocked,如果不在就切换线程,也会调用到 scheduleVsyncLocked 方法,而这个方法就是我们之前说过的申请 VSYNC 信号的方法了。
如果没有开启 VSYNC,则直接调用 doFrame 方法。
另外可以看到,刚才我们用到 Handler 发送消息的时候,都调用了 msg.setAsynchronous(true)方法,这个方法就是设置消息为异步消息。因为我们刚才一开始的时候设置了同步屏障,所以异步消息就会先执行,这里的设置异步也就是为了让消息第一时间执行而不受其他 Handler 消息影响。
小结 1
通过上面一系列方法,我们能得到一个初步的逻辑过程了:
ViewRootImpl 初始化的时候 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 ,会实例化 Choreographer 对象,也就是获取当前线程(一般就是主线程)对应的 Choreographer 对象。
Choreographer 初始化的时候,会新建一个当前线程对应的 Handler 对象,初始化 FrameDisplayEventReceiver,计算一帧的时间等一系列初始化工作。
当 UI 改变的时候,会调用到 ViewRootImpl 的 scheduleTraversals 方法,这个方法中加入了同步屏障消息,并且调用了 Choreographer 的 postCallback 方法去申请 VSYNC 信号。
在这个过程中,Handler 发送了延迟消息,切换了线程,并且给消息都设置了异步,保证最先执行。
继续看 scheduleVsyncLocked 方法。
scheduleVsyncLocked
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
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);
}
}
代码很简单,就是通过 FrameDisplayEventReceiver,请求 native 层面的垂直同步信号 VSYNC。
这个 FrameDisplayEventReceiver 是在 Choreographer 构造方法中实例化的,继承自 DisplayEventReceiver,主要就是处理 VSYNC 信号的申请和接收。
刚才说到调用 nativeScheduleVsync 方法申请 VSYNC 信号,然后当收到 VSYNC 信号的时候就会回调 onVsync 方法了。
onVsync(接收 VSYNC 信号)
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
//...
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);
}
}
这里同样通过 Handler 发送了一条消息,执行了本身的 Runnable 回调方法,也就是 doFrame()。
doFrame(绘制帧数据)
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
//...
//当前帧的 vsync 信号来的时间,假如为 12 分 200ms
long intendedFrameTimeNanos = frameTimeNanos;
//当前时间,也就是开始绘制的时间,假如为 12 分 150ms
startNanos = System.nanoTime();
//计算时间差,如果大于一个帧时间,则是跳帧了。比如是 50ms,大于 16ms
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
//计算掉了几帧,50/16=3 帧
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//计算一帧内时间差,50%16=2ms
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
//修正时间,vsync 信号应该来得时间,为 12 分 148ms,保证和绘制时间对应上
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
//信号时间已过,不能再绘制了,等待下一个 vsync 信号,保证后续时间同步上
scheduleVsyncLocked();
return;
}
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
//执行相关的 callback 任务
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这里主要的工作就是:
设置当前帧的开始绘制时间,上节说过开始绘制要在 vsync 信号来的时候开始,保证两者时间对应。所以如果时间没对上,就是发送了跳帧,那么就要修正这个时间,保证后续的时间对应上。
执行所有的 Callback 任务。
doCallbacks(执行任务)
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
mFrameIntervalNanos;
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
评论