異步消息可以理解爲是用來提升消息的重要性,在有異步消息時,會優先處理距當前時間最近的異步消息,同步消息都不會被處理,繪製界面就是一個異步消息;
同步消息:沒有設置爲異步的消息都是同步消息;
Message提供setAsynchronous用來設置異步消息;
一、異步消息的使用流程
1.使用MessageQueue的postSyncBarrier() 來加入同步屏障;同步屏障其實就是一個handler爲空的消息,它也存放在消息隊列中;
2.往消息隊列中加入異步消息,跟平時創建消息沒有什麼不同,只是額外調用setAsynchronous(true)來設置異步消息;
3.線程執行過程中,是怎麼取消息的,異步消息爲什麼會優先於同步消息;【介紹下原理,可以不關注】
Looper的loop會一直循環取消息進行處理,我們主要關注的是MessageQueue的next方法,它在消息隊列頭部是屏障消息的時候,會在它後面找最近的異步消息,如果時間合適的話就返回這個消息,如果時間不ok就休眠等待;
這裏有個隱含意思:同步屏障消息在隊列頭部纔有效,才需要處理異步消息,如果同步屏障消息不在頭部的話,不影響它前面的同步消息的處理;
可以看下next關鍵代碼:
Message msg = mMessages;
//target爲空表示是同步屏障消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
4.異步消息執行時,使用MessageQueue的removeSyncBarrier來刪除同步屏障;
二、繪製消息的處理過程
1.加入同步屏障;
我們都知道繪製消息都是在ViewRootImpl中處理的,這裏我們只看scheduleTraversals方法;
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//添加同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//設置繪製處理函數
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
2.設置異步消息;
硬件底層嚮應用層發出垂直同步信號後,會回調Choreographer內部類FrameDisplayEventReceiver的onVsync方法,其內部主要是向消息隊列中放入異步消息;異步消息執行時會調用FrameDisplayEventReceiver的run方法來處理;
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);
}
// TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
// 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;
}
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);
}
}
3.刪除同步屏障;
第2步中的doFrame會執行Choreographer裏設置的所有回調消息,包括第1步中設置的Choreographer.CALLBACK_TRAVERSAL消息;
這會執行到ViewRootImpl的TraversalRunnable,TraversalRunnable主要就是調用doTraversal方法;
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//刪除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}