Looper Handler MessageQueue分析
我所有的文章都會送出到github上,喜歡的同學可以來github關注。歡迎送出你們的文章
Github https://github.com/twolight/LearnNote.git
郵箱 [email protected]
背景
在Android,線程之間的通信,通過Hanlder發送消息,具體Hanlder是向那個線程發送消息,是根據Hanlder在哪個線程中建立的。這同時決定了Hanlder的handleMessage方法運作在哪個線程。
public class MyActivity extends Activity{
public void onCreate(...){
//在主線程中建立,Handler發送的消息是向主線程發送,主線程處理消息
Handler hanlder = new Hanlder();
}
}
Thread thread = new Thread(new Runnable(){
public void run(){
//建立Looper
Looper.prepare();
//在子線程中建立,Handler發送的消息是向子線程發送,子線程處理消息
Handler hanlder = new Hanlder();
//Looper開始循環,從消息隊列中開始取出消息處理
Looper.loop();
}
});
了解清楚之後,再來分析消息是如何發送到線程的,線上程中又是如何如何處理消息的。
分析
在上面的代碼中,之是以在Activity建立Hanlder之前沒有調用Looper.prepare()和Looper.loop(),是因為主線程在啟動時,已經自己建立Looper,并調用了loop。
是以我們知道,線程之間的通信,要經曆着幾個步驟。
- 建立Looper
- 建立Hanlder
- Looper開始循環(如果此時并沒有消息隊列并沒有消息,就線程就阻塞,直到有消息)
- Hanlder發送消息到消息隊列
- Looper開始處理消息(調用handler的dispatchMessage方法)
下面分析這幾個步驟
步驟分析
-
建立Looper
涉及到ThreadLocal的概念,如果想要深入了解ThreadLocal機制,可以自己去了解下,這裡簡要說下。public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException(...); } sThreadLocal.set(new Looper(quitAllowed)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ /** 這是Andorid主線程,自己調用的,我們不應該在代碼中調用 */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException(...); } sMainLooper = myLooper(); } }
Implements a thread-local storage, that is, a variable for which each thread
has its own value. All threads share the same {@code ThreadLocal} object,
but each sees a different value when accessing it, and changes made by one
thread do not affect the other threads. The implementation supports
{@code null} values.
ThreadLocal實作線程的本地變量存儲。通俗的來講,在一個Thread中ThreadLocal.set(變量),隻有這個線程能通過ThreadLocal.get拿出來這個變量。
Looper.prepare()的作用是,建立一個Looper對象,并将它放到目前線程的ThreadLocal中去。并且建立一個MessageQueue.
-
建立Hanlder
public Handler(Callback callback, boolean async) { ... mLooper = Looper.myLooper(); if (mLooper == null) { ... } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
由于建立Handler和建立Looper的線程是同一個線程,Handler能拿到之前建立的Looper對象,以及在Looper中建立的消息隊列。
是以這就是Threadlocal的作用,隻要在同一個線程内,對于不同的對象,都能通過Threadlocal拿到共享的變量。Looper,就是這樣的共享的變量。
-
Looper消息循環
關鍵代碼public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... msg.target.dispatchMessage(msg); ... } //next 方法代碼多,下面簡化成僞代碼 Message next(){ for(;;){ if(有消息){ return msg; }else{ } if(需要退出){ return null; } } } }
next()方法内是一個死循環,如果消息隊列沒有消息,那麼該方法不會傳回Message對象,代碼一直在其内部執行。如果MessageQueue正在退出,那麼傳回null。
loop方法,在死循環中,不停的從消息隊列取消息,沒有消息,代碼就一直在next方法中執行,不會有傳回值。代碼不會往下執行。next()如果傳回null,表示消息隊列正在退出,那麼Looper也需要退出,就跳出死循環停止對消息隊列取消息。next()傳回消息,那麼調用的msg.target.dispatchMessage(msg),也就是調用Handler的dispatchMessage方法,去分發msg。
-
Hanlder發送消息
Handler發送消息,最終會調用一下代碼
queue.enqueueMessage(msg, uptimeMillis);将消息方法消息隊列中,之前說的消息隊列的next方法,會一個死循環,不停地檢測是否有消息。隻要成功執行了queue.enqueueMessage,那麼Looper就能拿到發送的消息,并轉發消息。public class Handler{ ... private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //是以知道msg的target,就是發送這個消息的Handler。 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //将消息方法消息隊列中 return queue.enqueueMessage(msg, uptimeMillis); } ... }
- 消息處理
上面的步驟,說了Looper會調用msg.target.dispatchMessage(msg);讓Handler轉發消息,handler會調用handleMessage方法,這是一個空實作,我們可以在子類或者匿名類中重寫此方法。public class Handler{ /** * Subclasses must implement this to receive messages. */ // public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } }
總結
至此,線程間的消息機制,調用流程,就分析完了。