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樹遍歷。