Android面向面試複習----Handler詳解
Handler詳解
這篇文章緣起於一道面試題:
Android面試題 請解釋下單執行緒模型中Message、Handler、MessageQueue、Looper之間的關係
雖然能夠大致說明白,但是自己對答案也不太滿意,翻一翻原始碼,從原始碼角度剖析一番。
1. 概述Handler相關物件模型關係
首先看一下Handler、Looper、MessageQueue、Message的相關類圖
Handler中有兩個成員變數:mLooper、mQueue,分別對應的是Looper和MessageQueue的例項。
Looper中包含一個成員變數mQueue,對應的是MessageQueue的例項。
在建立Handler的時候會獲取當前執行緒的Looper,並將Looper對應的訊息佇列也賦值給Handler中的mQueue,用來儲存訊息。Handler的構造方法
平時我們都是new Handler()建立,最後還是呼叫的兩個引數的構造方法
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
在構造方法中通過
mLooper = Looper.myLooper();
建立了Looper。- myLooper方法中是從sThreadLocal中獲取的Looper:sThreadLocal.get()
Looper是在什麼地方建立的呢?答案是prepare中,所以在子執行緒中,沒有呼叫Looper.prepare()不能建立Handler,而主執行緒預設執行了Looper.prepare(),所以不會有這個問題。
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)); }
- sThreadLocal是跟執行緒相關的,所以,每個執行緒可以對應一個Looper及MessageQueue
通過Looper,獲取到了Looper對應的MessageQueue,並賦值給Handler中的MessageQueue
這樣就把Handler和Looper、MessageQueue串聯了起來。
2. 從原始碼角度剖析3個流程
傳送訊息
傳送訊息有兩種方式:
sendMessage(Message msg)
,或者post(Runnable r)
,最終都是呼叫到了sendMessageAtTime(Message msg, long uptimeMillis)
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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
其中最關鍵的是
queue.enqueueMessage(msg, uptimeMillis)
訊息佇列把Message放到了訊息佇列中,儲存起來。分發訊息
Looper呼叫了loop()方法之後,就會不停的從MessageQueue中讀取訊息,只要讀取到訊息,就會進行分發處理。下面我們把loop()中讀取訊息,分發處理邏輯摘出來。
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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... }
其中:
Message msg = queue.next();
從訊息佇列中取出訊息
如果訊息不為空,則使用Handler的dispatchMessage方法進行分發處理。public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
這裡主要有兩個分支:
handleCallback(msg)
是處理的Runnable任務handleMessage(msg)
是處理的普通訊息任務
處理訊息
處理訊息就是執行HandleMessage中的邏輯或者Runnable中的邏輯。
在看Looper.loop()方法邏輯的時候,最後一行被我刪掉了,這一行是等訊息分發處理完後對msg的回收處理。
msg.recycleUnchecked();
原始碼如下:
void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
3. 文章開頭的面試題答案整理
工作流程:
- 在單執行緒中,Looper輪詢器被呼叫prepare()和loop()後,它會不斷的從MessageQueue頭部讀取Message;
- 建立Handler時,Handler內部會持有當前執行緒對應的Looper和MessageQueue的引用。
- 如果該執行緒中有Handler傳送訊息給MessageQueue,Looper就能夠取出該訊息,通過Message的target(Handler)的dispathMessage,進行處理(兩個分支,一個處理Runnable,一個處理普通Message)。
- 處理完成後,Looper又繼續進行訊息的拉取,如此迴圈往復。直到呼叫
removeCallbacksAndMessages
可以將當前Handler中的所有任務給取消掉。