Handler 源碼分析

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 在該線程中運行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章