前言
Android的消息機制之前有一篇文章有寫,里面具體講到了Handler怎么發(fā)送和處理消息的整個過程。感興趣的同學可以先跳轉過去看看 從Handler.post(Runnable r)再一次梳理Android的消息機制(以及handler的內存泄露)
在看消息機制的時候,不管是把消息加入隊列,還是取出隊列,Message有個isAsynchronous方法一直沒關注,今天來看看這個方法到底是做什么的。
/**
* Returns true if the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
Handler同步屏障(SyncBarrier)
要理解這個方法的含義,我們要先了解一下Handler的同步屏障機制。通常我們使用Handler發(fā)消息的時候,都是用的默認的構造方法生成Handler,然后用send方法來發(fā)送消息,其實這時候我們發(fā)送的都是同步消息,發(fā)出去之后就會在消息隊列里面排隊處理。我們都知道,Android系統(tǒng)16ms會刷新一次屏幕,如果主線程的消息過多,在16ms之內沒有執(zhí)行完,必然會造成卡頓或者掉幀。那怎么才能不排隊,沒有延時的處理呢?這個時候就需要異步消息,在處理異步消息的時候,我們就需要同步屏障,讓異步消息不用排隊等候處理??梢岳斫鉃橥狡琳鲜且欢聣?,把同步消息隊列攔住,先處理異步消息,等異步消息處理完了,這堵墻就會取消,然后繼續(xù)處理同步消息。
怎么來使用這個同步屏障呢?在MessageQueue里面有postSyncBarrier方法:
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) {
final int token = mNextBarrierToken++;
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;
}
}
其實很簡單,就是創(chuàng)建了一個消息,但是值得注意的是,這個消息沒有target,普通的消息的必須有target的(不然交給誰來處理消息呢?具體的可以看開頭的文章鏈接)。然后我們來看看怎么取出消息。
處理異步消息
來到MessageQueue的next方法:
Message next() {
省略代碼
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
省略代碼
}
}
這里可以看到有一個很關鍵的判斷,上面我們知道屏障消息的target為空,所以這里判斷為true,while循環(huán)直到取出異步消息終止。接下來的處理就跟同步消息一樣了,這里不贅述。
怎么發(fā)送異步消息
那怎么來發(fā)送異步消息呢?Message有setAsynchronous方法可以直接設置為異步消息
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
還有就是可以看到Handler的構造方法
public Handler(boolean async)
public Handler(@Nullable Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
async參數(shù)可以控制是否發(fā)送異步消息,如果設置為true,Handler發(fā)送的都將是異步消息。
哪里有應用呢
我們平時要發(fā)送同步屏障postSyncBarrier需要反射才能使用
public void postSyncBarrier() {
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
token = (int) method.invoke(Looper.getMainLooper().getQueue());
}
public void removeSyncBarrier() {
Method method = MessageQueue.class
. getDeclaredMethod("removeSyncBarrier", int.class);
method.invoke(Looper.getMainLooper().getQueue(), token);}
}
在Android系統(tǒng)里面為了更快響應UI刷新在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();
}
}