API:28java
在阅读Android系统源码时发现,任何可能会引起View发生变化的操做,都会触发执行ViewRootImpl
中的scheduleTraversals()
方法,来安排下一次屏幕刷新信号到来的时候,对View树的遍历。android
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();
}
}
复制代码
咱们发现,在系统准备注册下一次屏幕刷新信号以前,往主线程的消息队列中发送了一个同步屏障消息。markdown
这么作的目的是什么呢?带着这样的疑问,咱们首先来看一下postSyncBarrier()
的源码。app
MessageQueue
位于android.os
包下,postSyncBarrier()
源码以下:less
/** 翻译官方注释: 直到在消息队列中遇到同步屏障时,全部的消息将正常处理。当遇到同步屏障时,同步屏障以后的同步消息将被暂停,得不到执行,直到调用了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)
释放掉同步屏障,全部的同步消息将继续执行。也就是说,同步屏障以后的异步消息将会优先获得执行!async
回到ViewRootImpl
中,在发送完同步屏障以后,系统经过Choreographer
调用postCallback()
发送了一个mTraversalRunnable
,确保mTraversalRunnable
能够第一时间获得执行。ide
在Choreographer
中postCallback()
方法最终实现是在postCallbackDelayedInternal()
方法内部,咱们来简单看一下postCallbackDelayedInternal()
方法内部的实现逻辑。oop
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()
方法,来注册下一次垂直同步信号的到来。
在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()
方法位于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
为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();
}
}
复制代码
搞清楚了同步屏障的做用以后,咱们接下来分析一下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()
的源码。
// 这里一样也标记为@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()
方法的源码:
// 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树遍历。