这是我参与更文挑战的第5天,活动详情查看: 更文挑战
ViewRootImpl
Android 中UI的刷新代表着界面的改变,一般有以下两个方法可以调用:
- requestLayout
- invalidate
而这两种方法都会执行到ViewRootImpl中,作为负责View树的绘制的顶层结构,上述两个方法都会调用到scheduleTraversals
中,scheduleTraversals可以翻译成“执行遍历”。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
复制代码
scheduleTraversals方法中主要做了三件事:
- mTraversalScheduled参数置为true。mTraversalScheduled的作用在于防止多次调用绘制。
- 向消息队列中添加一个同步屏障。这里的
mHandler
代表主线程,同步屏障的所用在于屏蔽同步消息,优先执行异步消息,那么后面与View相关的消息肯定都是异步的。毕竟要优先保证View的绘制,防止卡顿。 - 调用Choreographer的postCallback方法。
Choreographer
我们首先来看postCallback
方法中的参数mTraversalRunnable
,该对象继承自Runnable,run方法中调用了doTraversal
方法。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
复制代码
doTraversal方法中,恰好也做了三件事:
- mTraversalScheduled参数置为true。表示可以接受下一次的绘制操作了。
- 移除同步屏障。放开同步消息
- 执行View树的绘制。
performTraversals
方法中会从根布局开始执行View树结构的测量、布局和绘制操作。
通过上述对应的两个方法,我们可以猜测postCallback方法的作用就是执行doTraversal
方法,
但是具体什么时候执行呢?
首先我们要知道屏幕的刷新频率FPS,也就是1s内屏幕的刷新次数,目前大部分的手机的刷新频率都在60~120之间,按照刷新频率为60来算的话,大概每16ms就会刷新一次频率,Android中按照这个时间来下发VSync信号,那么可能大家就会有下面的疑问:
- View的绘制和Vsync信号之间的关系,View什么时候绘制?
- VSync信号如何接收,需要主动订阅吗?
通过以上两个问题,我们引出一个重要的类Choreographer
,我将它翻译为起舞者
,Android中对该类的说明是协调动画、输入和绘图的时序,接收一个垂直同步信号,计划下一帧的操作。简单就是Choreographer注册一个VSync信号接收器,接收下一帧的到来然后执行指定的操作。
Choreographer中postCallback
方法会执行到postCallbackDelayedInternal
方法。
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//将传递的参数添加到CallbackQueues中
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//如果执行时间<=当前时间,立即执行
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);
}
}
}
复制代码
- 首先将传递过来的参数callbackType、action、token添加到CallbackQueues中
- 按照执行时间来调用
scheduleFrameLocked
方法,时间未到的话就调用Handler来发送一个延迟的异步消息,最终还会执行到scheduleFrameLocked方法。
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
//是否是主线程
(isRunningOnLooperThreadLocked()) { //注册VSync接收器
scheduleVsyncLocked();
} else {
//切换到主线程注册VSync接收器
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);
}
}
}
复制代码
USE_VSYNC
默认为true,表示接收VSync信号,然后调用scheduleVsyncLocked
注册信号接收器。反之直接通过主线程的Handler调用doFrame
方法执行View的操作。
Choreographer中通过DisplayEventReceiver
注册VSync信号接收器,接收到下一帧的VSync信号时,会回调到onVsync
中。
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) {
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);
}
}
复制代码
DisplayEventReceiver实现了Runnable方法,在onVsync方法中通过Handler直接调用自身的run方法,然后执行doFrame
方法
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
......
//省略的代码用于执行帧的时间校正操作
......
try {
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);
}
}
复制代码
doCallbacks
方法中按照Type来分别执行,大家还记得我们调用postCallback
方法传递的type吗,没错就是Choreographer.CALLBACK_TRAVERSAL。
继续看一下doCallbacks中的操作:
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
}
复制代码
按照callbackType,获取对应的CallbackRecord 对象,然后执行run方法,run方法中会调用action,也就是我们最开始传递的mTraversalRunnable
的run方法。
走到这里整个流程就可以通了,在接收到VSync信号后,会调用ViewRootImpl的doTraversal
方法,继而执行View树结构的绘制操作。我们也可以回答上面提出的两个问题:
1. View的绘制和Vsync信号之间的关系,View什么时候绘制?
View的绘制首先会在消息队列中插入一条同步屏障,然后注册VSync信号接收器,接收到下一帧的VSync信号才会立即绘制,移除同步屏障。
2. VSync信号如何接收,需要主动订阅吗?
VSync需要注册信号接收器,才会主动下发。同时会根据当前时间进行同步Vsync信号。
在整个View绘制的流程中Choreographer
起到的作用:
- 接收和执行View相关的动画、输入和绘制。
- 注册VSync信号接收器和执行下一帧的操作,同时还负责同步VSync信号的时间。