API:28
在阅读Android系统源码时发现,任何可能会引发View发生变化的操作,都会触发执行ViewRootImpl中的scheduleTraversals()方法,来安排下一次屏幕刷新信号到来的时候,对View树的遍历。
scheduleTraversals()
ViewRootImpl位于android.view包下,scheduleTraversals()源码如下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 往主线程的消息队列中发送一个同步屏障
// mTraversalBarrier是ViewRootImpl中的成员变量,用于移除同步屏障时使用
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 确保mTraversalRunnable第一时间得到执行。这里的token为null,后面回调会用到
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
我们发现,在系统准备注册下一次屏幕刷新信号之前,往主线程的消息队列中发送了一个同步屏障消息。
这么做的目的是什么呢?带着这样的疑问,我们首先来看一下postSyncBarrier()的源码。
postSyncBarrier()源码分析
MessageQueue位于android.os包下,postSyncBarrier()源码如下:
/**
翻译官方注释:
直到在消息队列中遇到同步屏障时,所有的消息将正常处理。当遇到同步屏障时,同步屏障之后的同步消息将被暂停,得不到执行,直到调用了removeSyncBarrier(token)释放掉同步屏障之 后,同步消息将继续执行。
此方法用于立即延迟所有后续发布的同步消息的执行,直到满足释放同步屏障的条件为止。异步消息不受屏障的影响,并继续照常进行处理。此调用必须始终以相同的token来调用removeSyncBarrier(token),以确保消息队列恢复正常运行,否则应用程序可能会挂起!
*/
// 这是一个标记为@hide的隐藏方法,也就是说系统不允许我们调用,只允许自己用
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
// mNextBarrierToken是MessageQueue中的成员变量,从0开始递增
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse(); //同步屏障消息创建出来之后,直接标识为使用中
msg.when = when;
msg.arg1 = token;
/* 我们发现,这个特殊的同步屏障消息msg.target没有赋值,是为null的;正常的消息都是有target的。也就是说不需要对应的Handler来处理这个消息, 只是起到了一个标记的作用*/
Message prev = null;
Message p = mMessages;
if (when != 0) {
// 该循环执行完之后,就找到了同步屏障插入的消息队列的位置
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
// 消息队列不为空,插入同步屏障消息到消息队列中
msg.next = p;
prev.next = msg;
} else {
// 消息队列为空,消息队列指针指向同步屏障
msg.next = p;
mMessages = msg;
}
// 返回token,用以调用removeSyncBarrier(token)来释放同步屏障
return token;
}
}
通过上面对源码的注释,我们知道,postSyncBarrier()方法的作用就是,往消息队列中添加了一个特殊的Message消息,来实现同步屏障的效果。
同步屏障的作用就是为了确保:同步屏障之后的所有同步消息都将被暂停,得不到执行,直到调用了removeSyncBarrier(token)释放掉同步屏障,所有的同步消息将继续执行。也就是说,同步屏障之后的异步消息将会优先得到执行!
Choreographer.postCallback()源码分析
回到ViewRootImpl中,在发送完同步屏障之后,系统通过Choreographer调用postCallback()发送了一个mTraversalRunnable,确保mTraversalRunnable可以第一时间得到执行。
在Choreographer中postCallback()方法最终实现是在postCallbackDelayedInternal()方法内部,我们来简单看一下postCallbackDelayedInternal()方法内部的实现逻辑。
Choreographer位于android.view包下,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;
// 把我们的mTraversalRunnable记录到CallbackQueue回调队列中,token为null
// CallbackQueue回调队列按回调类型分类
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
// 因为postCallback()调用时delayMillis为0,所以dueTime==now
scheduleFrameLocked(now);
} else {
// 如果是一个延时消息,则往主线程消息队列发送一个异步消息,最终还是会执行到
// scheduleFrameLocked()方法
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
接下来我们再看一下scheduleFrameLocked()方法的具体实现。
private void scheduleFrameLocked(long now) {
// 如果已经注册了下一次屏幕刷新信号则跳过,避免重复注册
if (!mFrameScheduled) {
mFrameScheduled = true;
// USE_VSYNC是一个静态内部变量,默认情况下都为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.
// 如果是在UI线程,立即安排注册下一次垂直同步信号
// 如果不是在UI线程,往主线程添加一个异步消息,以尽快安排注册下一次垂直同步信号
if (isRunningOnLooperThreadLocked()) {
// 最终会调用DisplayEventReceiver中的nativeScheduleVsync()注册下一次垂直同步信号
scheduleVsyncLocked();
} else {
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);
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);
}
}
}
通过上面对源码的注释,我们发现最终会调用到DisplayEventReceiver中的nativeScheduleVsync()方法,来注册下一次垂直同步信号的到来。
FrameDisplayEventReceiver.onVsync()源码分析
在Choreographer中,当下一次垂直同步信号到来时,会回调FrameDisplayEventReceiver中的onVsync()方法,接下来,我们来看一下onVsync()方法的源码实现。
FrameDisplayEventReceiver为Choreographer中的私有成员内部类,onVsync()具体实现如下:
// 当接收到垂直同步信号时调用该方法
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
// Ignore vsync from secondary display.
// This can be problematic because the call to scheduleVsync() is a one-shot.
// We need to ensure that we will still receive the vsync from the primary
// display which is the one we really care about. Ideally we should schedule
// vsync for a particular display.
// At this time Surface Flinger won't send us vsyncs for secondary displays
// but that could change in the future so let's log a message to help us remember
// that we need to fix this.
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;
}
// 当接收到垂直同步信号之后,往主线程发送了一个异步消息;该异步消息的callback为
// FrameDisplayEventReceiver本身,因为FrameDisplayEventReceiver实现了Runnable接口
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
通过上面对源码的注释信息,我们发现,当垂直同步信号到来时,Android系统往主线程消息队列中发送了一个异步消息。而这个异步消息,也就是我们前面发送同步屏障的作用,来确保这个异步消息可以第一时间执行。
我们知道,目前所有的显示设备都有一个固定的刷新率,比如现在Android手机上常见的60HZ、90HZ和120HZ,简单来说,就是刷新率为多少HZ,就表示每秒钟刷新多少次,60HZ就是每秒钟刷新60次。为了确保整个显示效果的流畅顺滑,Android系统在每一次垂直信号到来时,尽可能都会在第一时间进行处理,来绘制界面的内容。我们View的onMeasure()、onLayout()和onDraw()三大流程,都是发生在这个过程中。所以,这三个方法如果比较耗时,超过了每帧平均耗时(每帧平均耗时=1000ms / 屏幕刷新率 ),则给用户的体验就是我们的应用比较卡顿,体验较差,也是我们着重优化的方向!
接下来,我们来看一下FrameDisplayEventReceiver中run()方法的具体实现逻辑。
@Override
public void run() {
// 重置标记值为false
mHavePendingVsync = false;
// 执行当前帧逻辑
doFrame(mTimestampNanos, mFrame);
}
doFrame()源码分析
doFrame()方法位于Choreographer中,源码如下:
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
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;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
// CALLBACK_INPUT事件首先回调
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
// 接下来回调CALLBACK_ANIMATION事件
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
// 再接下来回调CALLBACK_TRAVERSAL事件
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()的回调是有先后顺序的。想想看也是合理的!CALLBACK_INPUT的卡顿应该是最明显的,我们首先进行处理,CALLBACK_ANIMATION次之,最后是CALLBACK_TRAVERSAL。
我们再来看一下doCallbacks()的源码实现:
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
// 标记当前正在执行callback回调
mCallbacksRunning = true;
// Update the frame time if necessary when committing the frame.
// We only update the frame time if we are more than 2 frames late reaching
// the commit phase. This ensures that the frame time which is observed by the
// callbacks will always increase from one frame to the next and never repeat.
// We never want the next frame's starting frame time to end up being less than
// or equal to the previous frame's commit frame time. Keep in mind that the
// next frame has most likely already been scheduled by now so we play it
// safe by ensuring the commit time is always at least one frame behind.
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;
if (DEBUG_JANK) {
Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ " ms which is more than twice the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ " ms in the past.");
mDebugPrintNextFrameTimeDelta = true;
}
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) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
// 执行回调方法
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
// 重置callback回调标记值为false
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
CallbackRecord源码分析
CallbackRecord为Choreographer中的私有静态内部类,CallbackRecord中的run()方法的具体实现:
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
// 通过前面的分析,我们前面通过postCallback()发送的回调token为null,所以会执行ViewRootImpl中mTraversalRunnable的run()方法
// 我们现在终于明白,前面发送同步屏障,目的就是为了确保mTraversalRunnable的run()方法能够第一时间得到执行
((Runnable)action).run();
}
}
}
到这里,我们终于明白,我们在前面通过ViewRootImpl类中的scheduleTraversals()方法,发送的同步屏障消息,是为了确保mTraversalRunnable能够第一时间得到执行。其中,mTraversalRunnable为ViewRootImpl中成员变量,具体实现为TraversalRunnable。TraversalRunnable则为ViewRootImpl中成员内部类,具体实现如下:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
// 在这里,真正开始执行View树遍历
doTraversal();
}
}
doTraversal()
搞清楚了同步屏障的作用之后,我们接下来分析一下ViewRootImpl中真正开始执行View树遍历的地方,也就是在这里移除了上面添加的同步屏障,也就是doTraversal()方法的实现。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障消息,消息队列得以正常进行
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// 开始真正执行View树的遍历
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
removeSyncBarrier()源码分析
那么,这个移除同步屏障的内部逻辑又是怎么实现的呢?我们来分析一下removeSyncBarrier()的源码。
// 这里同样也标记为@hide,和postSyncBarrier()是一对儿,系统也不允许我们调用
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 这里循环执行完毕,就找到了对用token的同步屏障消息
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
// 如果这里为null,说明这个token对应的同步屏障消息要么还没有添加到消息队列中;要么已经被移除掉了,抛出异常!
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
// 从消息队列中移除同步屏障消息;当前消息循环已经在运行中,不需要再次唤醒
prev.next = p.next;
needWake = false;
} else {
// 同步屏障消息位于消息队列第一个,从消息队列中移除同步屏障。
// 当前消息循环为阻塞状态;如果下一个消息为null,或者下一个消息的target不为null,则唤醒消息循环
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
// 回收Message消息,循环利用
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
// 如果当前消息循环正在退出,则说明它已经唤醒了
// 我们认为当mQuitting为false时,mPtr肯定不为0,也就是正常状态
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
MessageQueue.next()
我们来看一下MessageQueue中获取消息时,遇到同步屏障时的处理逻辑。也就是next()方法的源码:
// API版本28
// android.os.MessageQueue.java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) { //当消息循环已经退出,则直接返回null
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//该处调用是Looper线程休眠的核心逻辑;当等待了nextPollTimeoutMillis时长之后,或者消息队列被唤醒,都会从该方法处返回
//当nextPollTimeoutMillis值为-1时,表示当前消息队列中没有要处理的消息,则会一直休眠下去,直到被唤醒
//当nextPollTimeoutMillis值为0时,最终会执行到native层的epoll_wait()方法,该方法会立即返回,不会进入休眠
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 特殊消息类型,表示消息队列中有同步屏障存在;此逻辑会找到同步屏障后第一个异步消息
// 如果没找到异步消息时,则会把nextPollTimeoutMillis赋值为-1,在下次轮询时,消息队列将进入阻塞
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// 当消息的触发时间大于当前时间时,则设置下一次轮询时的休眠时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
// 从消息队列链表中移除获取到的消息
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; //成功获取到MessageQueue中的下一条将要执行的消息
}
} else {
// No more messages.
nextPollTimeoutMillis = -1; // 没有消息,进入无线休眠,直到被唤醒
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) { //MessageQueue正在退出,则返回null
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
// 能执行到这里,说明消息队列在下一次轮询中马上就要进入休眠状态啦
// 当消息队列为空,或者还没到下一次消息的执行时间时,给pendingIdleHandlerCount重新赋值,准备执行IdleHandler中相关逻辑
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果没有IdleHandler需要执行,则标记当前消息队列为阻塞状态,进入下一次轮询,下一次轮询时会阻塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 初始化mPendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// 给即将要运行的IdleHandler数组任务赋值,通过mIdleHandlers转换为IdleHandler数组
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
// 当前消息队列处于空闲状态,执行IdleHandler逻辑
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 获取完之后,释放数组中对IdleHandler的引用
boolean keep = false;
try {
// 执行IdleHandler空闲逻辑,该方法需要返回一个boolean值
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 当IdleHandler不需要保存时,keep为false,执行完一次后,从mIdleHandlers移除该任务
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0; //重置pendingIdleHandlerCount为0,以保证该次获取消息时不会重复执行
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
// 当IdleHandler执行完时,可能已经到了下一个消息执行的时间,因此,在下一次轮询时无需等待,直接获取下一个消息
// 当nextPollTimeoutMillis为0时,nativePollOnce()方法底层逻辑会立即返回,不会阻塞休眠
nextPollTimeoutMillis = 0;
}
}
总结
我们对上面的分析流程做一个总结:
-
任何可能会引发View发生变化的操作,都会触发执行
ViewRootImpl中的scheduleTraversals()方法,来安排下一次屏幕刷新信号到来的时候,对View树的遍历。 -
在
scheduleTraversals()方法内部,会首先往主线程的消息队列中发送一个同步屏障,这个同步屏障其实就是一个特殊的Message(),这个特殊的msg.target没有赋值,是为null的;而正常的消息都是有target的。以此来起到一个同步屏障的目的:确保同步屏障之后的异步消息能优先得到执行!其实,最终是为了确保doTraversal()优先得到执行,去真正地开始执行View树遍历。 -
在
doTraversal()方法中,移除了前面scheduleTraversals()方法中发送的同步屏障,确保消息队列得以正常继续运行;接下来调用performTraversals()真正地开始执行View树遍历。