承接上一篇Handler系列一,上篇主要總結了Handler如何通信,這篇來介紹Handler怎么通信。
Handler的通信機制
Handler,Looper,MessageQueue如何關聯
Handler 通信機制
創建Handler,并采用當前線程的Looper創建消息循環系統;
Handler通過sendMessage(Message)或Post(Runnable)發送消息,調用enqueueMessage把消息插入到消息鏈表中;
Looper循環檢測消息隊列中的消息,若有消息則取出該消息,并調用該消息持有的handler的dispatchMessage方法,回調到創建Handler線程中重寫的handleMessage里執行。
Handler 如何關聯 Looper、MessageQueue
Handler及其關聯的類圖
以上類圖可以快速幫助我們理清Handler與Looper、MessageQueue的關系,以下從源碼的角度慢慢分析:
1、Handler 發送消息
上一段很熟悉的代碼:
Message msg =Message.obtain(); //從全局池中返回一個message實例,避免多次創建message(如new Message) msg.obj = data; msg.what=1; //標志消息的標志 handler.sendMessage(msg);從sendMessageQueue開始追蹤,函數調用關系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,攜帶者傳來的message與Handler的mQueue一起通過enqueueMessage進入隊列了。
對于postRunnable而言,通過post投遞該runnable,調用getPostMessage,通過該runnable構造一個message,再通過 sendMessageDelayed投遞,接下來和sendMessage的流程一樣了。
2、消息入隊列
在enqueueMessage中,通過MessageQueue入隊列,并為該message的target賦值為當前的handler對象,記住
msg.target很重要,之后Looper取出該消息時,還需要由msg.target.dispatchMessage回調到該handler中處理消息。PRivate boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}在MessageQueue中,由Message的消息鏈表進行入隊列
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true;}3、Looper 處理消息
再說處理消息之前,先看Looper是如何構建與獲取的:
構造Looper時,構建消息循環隊列,并獲取當前線程
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }但該函數是私有的,外界不能直接構造一個Looper,而是通過Looper.prepare來構造的:
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }這里創建Looper,并把Looper對象保存在sThreadLocal中,那sThreadLocal是什么呢?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();它是一個保存Looper的TheadLocal實例,而ThreadLocal是線程私有的數據存儲類,可以來保存線程的Looper對象,這樣Handler就可以通過ThreadLocal來保存于獲取Looper對象了。
TheadLocal 如何保存與獲取Looper?
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }在 set 中都是通過
values.put保存當前線程的 Looper 實例,通過values.getAfterMiss(this)獲取,其中put和getAfterMiss都有key和value,都是由Value對象的table數組保存的,那么在table數組里怎么存的呢?table[index] = key.reference; table[index + 1] = value;很顯然在數組中,前一個保存著ThreadLocal對象引用的索引,后一個存儲傳入的Looper實例。
接下來看Looper在loop中如何處理消息
在
loop中,一個循環,通過next取出MessageQueue中的消息若取出的消息為null,則結束循環,返回。
設置消息為空,可以通過MessageQueue的quit和quitSafely方法通知消息隊列退出。
若取出的消息不為空,則通過msg.target.dispatchMessage回調到handler中去。
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x"Long.toHexString(ident) + " to 0x"
Long.toHexString(newIdent) + " while dispatching to "
msg.target.getClass().getName() + " "
msg.callback + " what=" + msg.what);
} msg.recycleUnchecked(); }}4、handler處理消息
Looper把消息回調到handler的dispatchMessage中進行消息處理:
若該消息有callback,即通過Post(Runnable)的方式投遞消息,因為在投遞
runnable時,把runnable對象賦值給了message的callback。若handler的mCallback不為空,則交由通過
callback創建handler方式去處理。否則,由最常見創建handler對象的方式,在重寫handlerMessage中處理。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}總結
以一個時序圖來總結handler的消息機制,包含上述如何關聯Looper和MessageQueue的過程。
Handler-Looper-MessageQueue時序圖
新聞熱點
疑難解答