前言
Android的消息機(jī)制之前有一篇文章有寫(xiě),里面具體講到了Handler怎么發(fā)送和處理消息的整個(gè)過(guò)程萎攒。感興趣的同學(xué)可以先跳轉(zhuǎn)過(guò)去看看 從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)
在看消息機(jī)制的時(shí)候挪略,不管是把消息加入隊(duì)列链瓦,還是取出隊(duì)列,Message有個(gè)isAsynchronous方法一直沒(méi)關(guān)注搓谆,今天來(lái)看看這個(gè)方法到底是做什么的刁愿。
/**
* 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)
要理解這個(gè)方法的含義绰寞,我們要先了解一下Handler的同步屏障機(jī)制。通常我們使用Handler發(fā)消息的時(shí)候,都是用的默認(rèn)的構(gòu)造方法生成Handler克握,然后用send方法來(lái)發(fā)送消息蕾管,其實(shí)這時(shí)候我們發(fā)送的都是同步消息枷踏,發(fā)出去之后就會(huì)在消息隊(duì)列里面排隊(duì)處理菩暗。我們都知道,Android系統(tǒng)16ms會(huì)刷新一次屏幕旭蠕,如果主線程的消息過(guò)多停团,在16ms之內(nèi)沒(méi)有執(zhí)行完,必然會(huì)造成卡頓或者掉幀掏熬。那怎么才能不排隊(duì)佑稠,沒(méi)有延時(shí)的處理呢?這個(gè)時(shí)候就需要異步消息旗芬,在處理異步消息的時(shí)候舌胶,我們就需要同步屏障,讓異步消息不用排隊(duì)等候處理疮丛♂I可以理解為同步屏障是一堵墻,把同步消息隊(duì)列攔住誊薄,先處理異步消息履恩,等異步消息處理完了,這堵墻就會(huì)取消呢蔫,然后繼續(xù)處理同步消息切心。
怎么來(lái)使用這個(gè)同步屏障呢?在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;
}
}
其實(shí)很簡(jiǎn)單片吊,就是創(chuàng)建了一個(gè)消息绽昏,但是值得注意的是,這個(gè)消息沒(méi)有target俏脊,普通的消息的必須有target的(不然交給誰(shuí)來(lái)處理消息呢全谤?具體的可以看開(kāi)頭的文章鏈接)。然后我們來(lái)看看怎么取出消息联予。
處理異步消息
來(lái)到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());
}
省略代碼
}
}
這里可以看到有一個(gè)很關(guān)鍵的判斷啼县,上面我們知道屏障消息的target為空,所以這里判斷為true沸久,while循環(huán)直到取出異步消息終止季眷。接下來(lái)的處理就跟同步消息一樣了,這里不贅述卷胯。
怎么發(fā)送異步消息
那怎么來(lái)發(fā)送異步消息呢子刮?Message有setAsynchronous方法可以直接設(shè)置為異步消息
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
還有就是可以看到Handler的構(gòu)造方法
public Handler(boolean async)
public Handler(@Nullable Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
async參數(shù)可以控制是否發(fā)送異步消息,如果設(shè)置為true,Handler發(fā)送的都將是異步消息挺峡。
哪里有應(yīng)用呢
我們平時(shí)要發(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)里面為了更快響應(yīng)UI刷新在ViewRootImpl.scheduleTraversals也有應(yīng)用:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}