歡迎訪問個人我的博客 傳送門html
作過 Android 開發的童鞋都知道,不能在非主線程修改 UI 控件,由於 Android 規定只能在主線程中訪問 UI ,若是在子線程中訪問 UI ,那麼程序就會拋出異常java
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy .
而且,Android 也不建議在 UI 線程既主線程中作一些耗時操做,不然會致使程序 ANR 。若是咱們須要作一些耗時的操做而且操做結束後要修改 UI ,那麼就須要用到 Android 提供的 Handler 切換到主線程來訪問 UI 。所以,系統之因此提供 Handler,主要緣由就是爲了解決在子線程中沒法訪問 UI 的問題。android
要理解 Handler 消息機制原理 還須要瞭解幾個概念:安全
UI 線程數據結構
主線程 ActivityThreadapp
Message異步
Handler 發送和處理的消息,由 MessageQueue 管理。async
MessageQueueide
消息隊列,用來存放經過 Handler 發送的消息,按照先進先出執行,內部使用的是單鏈表的結構。oop
Handler
負責發送消息和處理消息。
Looper
負責消息循環,循環取出 MessageQueue 裏面的 Message,並交給相應的 Handler 進行處理。
在應用啓動時,會開啓一個 UI 線程,而且啓動消息循環,應用不停地從該消息列表中取出、處理消息達到程序運行的效果。
Looper 負責的就是建立一個 MessageQueue,而後進入一個無限循環體不斷從該 MessageQueue 中讀取消息,而消息的建立者就是一個或多個 Handler 。
流程圖以下:
下面結合源碼來具體分析
Looper 比較重要的兩個方法是 prepare( ) 和 loop( )
先看下構造方法
final MessageQueue mQueue; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper 在建立時會新建一個 MessageQueue
經過 prepare 方法能夠爲 Handler 建立一個 Lopper,源碼以下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //一個線程只能有一個looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
能夠看到這裏建立的 Looper 對象使用 ThreadLocal 保存,這裏簡單介紹下 ThreadLocal。
ThreadLocal 是一個線程內部的數據存儲類,經過它能夠在指定的線程中存儲數據,數據存儲之後,只有在指定線程中能夠獲取到存儲的數據,對於其餘線程來講則沒法獲取到數據,這樣就保證了一個線程對應了一個 Looper,從源碼中也能夠看出一個線程也只能有一個 Looper
,不然就會拋出異常。
prepareMainLooper() 方法是 系統在 ActivityThread 中調用的。
ActivityThread.java
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); //...省略代碼 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
由此能夠看出,系統在建立時,會自動建立 Looper 來處理消息,因此咱們通常在主線程中使用 Handler 時,是不須要手動調用 Looper.prepare() 的。這樣 Handler 就默認和主線程的 Looper 綁定。
當 Handler 綁定的 Looper 是主線程的 Looper 時,則該 Handler 能夠在其 handleMessage 中更新UI,不然更新 UI 則會拋出異常。
在開發中,咱們可能在多個地方使用 Handler,因此又能夠得出一個結論:一個 Looper 能夠和多個 Handler 綁定
,那麼 Looper 是怎麼區分 Message 由哪一個 Handler 處理呢?
繼續看源碼 loop()
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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch took " + time + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } 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();// 回收消息 } }
代碼比較多,咱們撿重點看。
2~6 行 獲取當前 Looper 若是沒有則拋異常,有則獲取消息隊列 MessageQueue
因此若是咱們在子線程中使用 Handler 則必須手動調用 Looper.prepare() 和 Looper.loop()
系統在代碼中提供了示例代碼
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
獲取到 MessageQueue 後開啓消息循環,不斷從 MessageQueue 中取消息,無則阻塞,等待消息。有則調用 msg.target.dispatchMessage(msg) 處理消息。
msg.target 就是 Message 所屬的 Handler,這個會再後面具體介紹 Handler 中會說明
因此上面的問題就能夠回答了,Looper 不須要考慮怎麼區分 Message 由哪一個 Handler 處理,只負責開啓消息循環接收消息並處理消息便可。處理完消息後會調用 msg.recycleUnchecked() 來回收消息。
那麼開啓消息循環後,能夠中止嗎?
答案是確定的,Looper 提供了 quit() 和 quitSafely() 來退出。
public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); }
能夠看到實際上調用的是 MessageQueue 中的退出方法,具體會在 MessageQueue 中介紹。
調用 quit() 會直接退出 Looper,而 quitSafely() 只是設定一個退出標記,而後把消息隊列中的已有消息處理完畢後才安全地退出。在 Loooper 退出後,經過 Handler 發送消息會失敗。若是在子線程中手動建立了 Looper ,則應在處理完操做後退出 Looper 終止消息循環。
到此 Looper 的源碼分析就完了,咱們來總結下 Looper 所作的工做:
Message 是線程通訊中傳遞的消息,它有幾個關鍵點
MessageQueue
MessageQueue 負責管理消息隊列,經過一個單鏈表的數據結構來維護。
源碼中有三個主要方法:
next 方法
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; 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()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } //...省略代碼 } }
能夠發現 next 方法是一個無限循環的方法,若是消息隊列中沒有消息,那麼 next 方法會一直阻塞在這裏。當有新消息到來時,next 方法會從中獲取消息出來返回給 Looper 去處理,並將其從消息列表中移除。
quit方法
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked();//移除還沒有處理的消息 } else { removeAllMessagesLocked();//移除全部消息 } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } } private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; } private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) { removeAllMessagesLocked(); // 移除還沒有處理的消息 } else { // 正在處理的消息不作處理 Message n; for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } }
從 上述代碼中能夠看出,當 safe 爲 true 時,只移除還沒有觸發的全部消息,對於正在處理的消息不作處理,當 safe 爲 false 時,移除全部消息。
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()); } } //獲取當前線程的 Looper 實例,若是不存在則拋出異常 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //關聯 Looper 中的 MessageQueue 消息隊列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
構造方法主要有三個參數
從源碼中可看出,由於 UI 線程在啓動時會自動建立 Looper 實例,因此通常咱們在 UI 線程中使用 Handler 時不須要傳遞 Looper 對象。而在子線程中則必須手動調用 Looper.prepare 和 Looper.loop 方法,並傳遞給 Handler ,不然沒法使用,這一點確定有很多童鞋都遇到過。
在拿到 Looper 對象後,Handler 會獲取 Looper 中的 MessageQueue 消息隊列,這樣就和 MessageQueue 關聯上了。
關聯上 MessageQueue ,接下來那咱們就看下 Handler 是如何發送消息的。
Handler 發送消息方法不少,實際上最後都是調用的 enqueueMessage 方法,看圖說話
主要看 enqueueMessage 方法
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); }
能夠看到在發送消息時給 Message 設置了 target = this 也就是當前的 Handler 對象,並調用了 MessageQueue 的 enqueueMessage 方法,這樣就把消息存在消息隊列,而後由 Looper 處理了。
童鞋們應該記得以前在講 Looper 時,說到 Looper 開啓消息循環後,會不斷從 MessageQueue 中取出Message,並調用 msg.target.dispatchMessage(msg) 來處理消息。
接下來,就來看看 Handler 是如何接收消息的也就是 dispatchMessage 方法
public interface Callback { public boolean handleMessage(Message msg); } /** * 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); } }
能夠看出 Handler 接收消息,只是調用一個空方法 handleMessage 是否是有些眼熟呢,看下咱們寫過不少次的 Handler 代碼
private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case value: break; default: break; } } };
沒錯這就是咱們本身建立 Handler 時重寫的方法,由咱們來處理消息,而後根據 msg.what 標識進行消息處理。
應用啓動時會啓動 UI 線程也就是主線程 ActivityThread,在 ActivityThread 的 main 方法中會調用 Looper.prepareMainLooper( ) 和 Looper.loop ( ) 啓動 Looper 。
Looper 啓動時會建立一個 MessageQueue 實例,而且只有一個實例,而後不斷從 MessageQueue 中獲取消息,無則阻塞等待消息,有則調用 msg.target.dispatchMessage(msg) 處理消息。
咱們在使用 Handler 時 須要先建立 Handler 實例,Handler 在建立時會獲取當前線程關聯的 Looper 實例 ,和 Looper 中的消息隊列 MessageQueue。而後在發送消息時會自動給 Message 設置 target 爲 Handler 自己,並把消息放入 MessageQueue 中,由 Looper 處理。Handler 在建立時會重寫的
handleMessage 方法中處理消息。
若是要在子線程中使用 Handler 就須要新建 Looper 實例,傳給 Handler 便可。
再看下流程圖
因篇幅較長,童鞋們的 Handler 確定用的爐火純青了,因此最後就不寫例子了。本人寫博客不久,寫的很差的地方,望各位海涵。