在 android Handler 介紹(一) 中的第一個例子:首先通過一個 無參 Handler 構造函數實例化一個 Handler 類型的全局變量,並重寫其 handleMessage 方法,在某一方法內調用 Handler 的 sendEmptyMessage 或者 sendMessage 發送消息,在某一時刻 handleMessage 回調方法會被調用 。
Handler 內部定義的兩個 final 對象
final MessageQueue mQueue ;
final Looper mLooper ;
MessageQueue 即爲其內部的消息隊列, mQueue 是通過 Looper 的靜態變量 mQueue 直接賦值的。而 mLooper 是通過 Looper 的靜態方法 myLooper() 賦值。進入該方法,
Looper.myLooper() 實質上調用了 sThreadLocal.get() 來獲取與當前線程相關聯的 Looper ,源碼裏面有這樣一句話
// sThreadLocal.get() will return null unless you've called prepare().
,在調用該方法之前必須先調用 prepare() 方法,否則該方法返回 null 。但事實上我們並沒有調用這個方法,卻能成功,原因是爲什麼呢?
Looper 內部提供了一個靜態方法 prepareMainLooper ,這個方法是在應用程序創建的時候直接由 android 系統調用的。
/** 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.
*/
該方法調用了 prepare () 方法和 setMainLooper (myLooper ()) 並將 myLooper () 返回的值設置爲主線程的 looper 。
接下來看 sendEmptyMessage 和其他幾個發送消息的方法,它們最終都會調用到 sendMessageAtTime 這樣一個方法,而這個方法會調用 MessageQueue 提供的 enqueueMessage 方法,該方法將發送的消息放入消息隊列裏面,最後調用
nativeWake(mPtr) 方法,這是一個 C++ 實現的方法,具體的實現內容沒有深究,但應該間接調用到 Looper 的 loop() 方法。
接下來看一下 loop() 方法,這個方法從消息隊列中取出消息,然後調用裏面存放的 target 的 dispatchMessage 方法:
msg.target.dispatchMessage(msg);
msg.target 存放的是在構造消息的類的 handler 對象,事實上這裏回調了 handler 的 dispatchMessage 方法,該方法會調用 handleMessage 方法。
android Handler 介紹(一) 中的第二個例子:定義一個 runnable ,並重寫其 run 方法,通過 Handler 的 post 方法或 postDelayed 方法來實現消息處理。
與 sendEmptyMessage 方法類似, post 方法或 postDelayed 方法最終也會調用到 sendMessageAtTime 方法。稍微有點不同的是,發送的消息的結構有點不一樣。 post 方法通過調用 getPostMessage(Runnable r) 獲取消息,該方法把 runnable 對象封裝成 Message 。接下來的處理過程與上面類似。在 dispatchMessage 方法裏處理的時候,會先判斷收到的消息中的 runnable 是不是空,如果不爲空,則先取出,調用, runnable 內部重寫的 run 方法:
message.callback.run();
如果在 run 方法中再調用方法,上述流程會重新再走一遍,這就是第二個例子爲什麼會反覆運行的原因了。
接下來分析線程中的 handler 。默認情況下,線程與 looper 並不直接關聯(主線程除外),線程通過調用 Looper.prepare() 方法來與 looper 建立聯繫。在 Looper 裏面定義了一個全局的靜態類 sThreadLocal
private static final ThreadLocal sThreadLocal = new ThreadLocal();
ThreadLocal 實現了線程的本地存儲,也就是說,對於不同的線程,同一個變量有不同的值。所有的線程共享一個共同的 ThreadLocal 對象,但訪問不同的線程時可以得到不同的值,修改這個值並不影響到其他線程。
Looper.prepare() 方法通過調用 sThreadLocal.set(new Looper()) ,將一個 looper 與所在的線程組成對放入 ThreadLocal 中, ThreadLocal 維護了一張 Object 類型的 table 表用來存放這兩個對象, Looper.myLooper() 調用其 get 方法將其取出。在另起的線程中,如果不調用 Looper.prepare() 方法,則從裏面取出的爲 null 。
通過之前的分析,重寫後的 handleMessage 是被 Handler 裏面的 dispatchMessage 方法調用的,而 dispatchMessage 方法被 Looper 裏面的 loop() 所調用,換句話說, Handler 裏面的 mLooper 所在的線程決定了 handleMessage 方法所在的線程,如果我們在構造 handler 的時候傳入一個非主線程相關聯的 looper 則 handleMessage 在該線程中運行。