android handler、looper異步消息處理機制
可能萌新感覺異步消息處理這個東西很高端,其實我們平時一直在用,最常用的該是子線程通知ui線程刷新了吧。sendMessage、handleMessage是不是突然間感覺就來了。
異步消息處理當(dāng)然離不開handler、looper、message以及messageQueue、threadLocal.....
那么先從handler開始
handler的主要作用其實就是sendMessage handleMessage。不過呢發(fā)送msg有好幾個函數(shù)(androidStuido輸入handler.send會彈出好幾個方法),最后基本上都是走這個關(guān)鍵的函數(shù)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
可以看到最后是要enqueueMessage(queue, msg, uptimeMillis);的,可以理解為把這次handler發(fā)送的msg發(fā)到對應(yīng)的queue(messageQueue)里面,延遲時間為uptimeMillis。
說到messageQueue,要有這個意識(暫且記住,后面會詳解)
handler必須持有一個looper,這個是在構(gòu)造handler的時候就會拿到的
不管是否是ui線程的mainLooper,handler使用的時候一定要有l(wèi)ooper,比如Toast在子線程使用時可以不使用ui線程的mainLooper,但是必須有一個looper,不然會拋出異常。looper和一個massageQueue對應(yīng),并且和一個線程對應(yīng)(注意了啊和線程相關(guān)的來了),一個線程最多有一個looper
由上面三條我們暫且可以得到這個信息:在創(chuàng)建handler的線程會綁定一個looper,既然有l(wèi)ooper肯定有一個專門的messageQueue.那么假如我在子線程Anew了一個handler(前提是該線程要prepare一個looper),那么這個子線程A就會有一個在該線程一直循環(huán)的looper來處理該線程messageQueue里面的msg。
然后如果我在另外一個線程B獲取到了該handler的對象,然后發(fā)送一個msg,因為handler、looper、messageQueue是對應(yīng)的,所以發(fā)送的msg最后是入隊到了創(chuàng)建該handler線程A的messageQueue上。已經(jīng)切到A線程就很簡單了啊,queue里面的msg取出來處理就完了。
好,現(xiàn)在我們已經(jīng)發(fā)送msg,并且送到對應(yīng)的queue里面了。接下來,Looper會一直循環(huán)的詢問它的messageQueue是否來消息了,一旦來了就會把msg取出來,然后讓它對應(yīng)的handler.dispatchMessage來分發(fā)這個消息。
直接上源碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到我們經(jīng)常用的handleMessage(msg)是在最后,需要前面的都放棄,才會執(zhí)行到它。那么前面的msg.callback以及mCallback是什么呢?
先說msg.callback
查看Message源碼發(fā)現(xiàn)msg.callback實際上是一個Runnable;不知道大家除了handler.sendMessage 以及handleMessage之外 handler.post(Runnable)、view.post(Runnable)、runOnUiThread(Runnable)有沒有使用過;這些都是可以實現(xiàn)在子線程通知ui線程進行更新UI操作。
這里面的Runnable參數(shù)就是msg.callback。其實上面3種方式到后面都調(diào)用了handler.post,那我們就看一下這個是干什么的
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
可以看到把runnable設(shè)置給msg后,到后面又是sendMessageAtTime(不記得的往上翻)。
所以啊,如果以上面三種方式,handler.handleMessage是不會觸發(fā)的哦,因為根本就沒走到那里,而是走了handleCallback(msg);`
private static void handleCallback(Message message) {
message.callback.run();
}
請牢記,通過handler把消息放到了對應(yīng)的msgQueue里面的時候,因為msgQueue和Looper是綁定的,所以等到looper用對應(yīng)的handler dispatch消息的時候,已經(jīng)切到了創(chuàng)建looper的線程了。
再簡單說一下mCallback
這其實是一個回調(diào)接口,創(chuàng)建handler的時候可以傳幾個參數(shù):looper、mcallBack、flag。平時我們什么默認值,其中mcallBack=Null;當(dāng)然也可以實現(xiàn)這個回調(diào)接口,重寫回方法
像這樣:
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.v("tag","uiThread 接口回調(diào) :"+Thread.currentThread().getId());
return true;
}
}){
@Override
public void handleMessage(Message msg) {
// super.handleMessage(msg);
Log.v("tag","uiThread 重寫handleMessage:"+Thread.currentTead().getId());
}
};
再看上面dispatchMessage方法回調(diào)方法返回false則會繼續(xù)執(zhí)行到handler.handlerMessage();返回true就直接return了。
現(xiàn)在我們對handler的發(fā)消息和處理消息應(yīng)該有比較清晰的認識了。只是還需合Looper來看。提到一個looper對應(yīng)一個線程那一定要看ThreadLocal。 這些下次再詳細講解。
上面提到的幾種發(fā)送消息的方式基本上最后都是執(zhí)nqueuessage(queue, msg, 0);是因為有一種方式是sendMessageAtFrontOfQueue(Message msg)它會直接到消息入隊那里 enqueueMessage(queue, msg, 0);
題外話:源碼什么的,不懂就反復(fù)看;能點進去就帶著好奇心點進去看看,如果是紅的,點不動到frameWork了,就暫時不要導(dǎo)入frameWork源碼深入了,這個有點多。如果最后點進去是native方法,涉及到JNI了,也暫停。等后面熟悉了sdk的源碼再接觸frameWork和jni。