SyncBarrier作用是保證隊列中的異步類型msg的優先執行,提高特殊msg的優先級。
一、postSyncBarrier()方法
該方法是hide的,業務不能直接使用,可以通過反射方式進行調用。調用該方法,會在消息隊列中插入一個空msg。與該方法對應的public void removeSyncBarrier(int token) ;
方法是刪除token對應的msg。
private int postSyncBarrier(long when) {
// 保證多線程的正確性,加鎖
synchronized (this) {
// 返回一個int型值,一個自增的成員變量
final int token = mNextBarrierToken++;
// 獲取一個空msg,target==null,插入到隊列中
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
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;
}
return token;
}
}
二、MessageQueue的next()方法
設置的屏障是在出隊的時候使用的,在出隊時,判斷隊頭消息是否是空消息(說明設置了SyncBarrier),如果是,則按順序執行隊列後面的異步消息,直到SyncBarrier被remove,纔會執行隊列中的同步消息。
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// msg.target == null 說明當前隊頭是一個空消息,被設置了SyncBarrier
if (msg != null && msg.target == null) {
// 找到該消息後面的異步消息(msg.isAsynchronous()方法)
do {
// 標識前一個消息
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 還未到消息執行時間,則阻塞這段時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
// prevMsg不爲空,從隊列中刪除即將執行的msg,但是隊頭保持不變,即在下一個循環中,隊頭還是個空消息
// 從這裏可以看出,如果被設置了SyncBarrier,只有當SyncBarrier被移除的時候,纔會執行隊列後面的非異步消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 替換隊頭
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 隊列中沒有可用消息
nextPollTimeoutMillis = -1;
}
}
}
}
三、SyncBarrier在UI刷新中的使用
1、UI有刷新需要時,首先會執行ViewRootImpl的scheduleTraversals()
方法,接收vsync信號也是通過handler機制(且msg是異步的,在Choreographer類中),所以此處設置了一個屏障,會使從當前時間點,到接收到vsync信號這段時間所有被插入到消息隊列中的同步消息失效,直到接收到vsync信號後,把屏障移除。這樣就保證了UI刷新信號的高優先級,保證了UI刷新的及時性。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 設置屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 下一個vsync信號會回調mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
// 該方法在接收到vsync信號時調用,執行view的measure、layout和draw
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}