Android 的訊息機制
Android 的訊息機制主要是指 Handler 的執行機制,Handler 的執行需要底層的 MessageQueue 和 Looper 來支援。
MessageQueue 是一個訊息的儲存單元,Looper 以無限迴圈的形式去查詢是否有新的訊息,有的話就處理,沒有就一直等待。Looper 中有一個特殊的概念 - ThreadLocal
執行緒預設是沒有 Looper 的,如果需要使用 Handler 就必須為執行緒建立 Looper。Android 主執行緒也叫 UI 執行緒,確切的說應該是 ActivityThread,其被建立時就會初始化 Looper,這就是主執行緒預設可以使用 Handler 的原因。
Android 的訊息機制概述
- Android 系統不允許子執行緒更新 UI 是因為 UI 控制元件不是執行緒安全的。
- Handler 建立時會採用當前所線上程的 Looper 來構建內部訊息迴圈系統,如果當前執行緒沒有 Looper,則會報錯。
- Handler 的 post 方法將一個 Runnable 投遞到 Handler 內部的 Looper 中去處理,其實最終都是通過 send 方法來完成的。
- Handler 的 send 方法呼叫時,會呼叫 MessageQueue 的 enqueueMessage 方法將這個訊息放入訊息佇列,然後 Looper 就發現有新訊息,接著處理這個訊息,最終訊息中的 Runnable 或者 Handler 的 handleMessage 方法就會被呼叫,這樣一來就實現了執行緒切換。
Android 的訊息機制分析
ThreadLocal 的工作原理
- ThreadLocal 是一個執行緒內部的資料儲存類,儲存資料後,只有在指定執行緒才能獲取資料,其他執行緒無法獲取。
- 當某些資料是以執行緒為作用域並且不同執行緒具有不同的資料副本時可以使用 ThreadLocal。
- 不同執行緒中訪問同一個 ThreadLocal 的 set 和 get 方法,它們對 ThreadLocal 所做的讀寫操作僅限於各自執行緒內部,這就是互不干擾的原因。
訊息佇列的工作原理
- 訊息佇列在 Android 中指的是 MessageQueue,MessageQueue 主要包含兩個操作:插入(enqueueMessage)和讀取(next)。
- next 讀取訊息的同時會將其從訊息佇列中刪除。
- MessageQueue 的內部實現並不是佇列,而是通過一個單鏈表的資料結構來維護訊息列表。
- next 方法是一個無線迴圈的方法,沒有訊息時一直阻塞,當有新訊息到來,next 會返回這條訊息並將其從單鏈表中移除。
Looper 的工作原理
- Looper 不停地從 MessageQueue 中檢視是否有新的訊息,如果有就立即處理,否則就一致阻塞在那裡。
-
為執行緒建立 Looper:
new Thread() { @Override public void run() { Looper.prepare(); // 建立 Looper Handler handler = new Handler(); Looper.loop(); // 開啟訊息迴圈 } }.start();
-
Looper.prepareMainLooper()
可以給主執行緒 ActivityThread 建立 Looper。 - 獲取主執行緒 Looper 可以通過
Handler handler = new Handler(Looper.getMainLooper());
。 - Looper 提供了兩個方法退出
quit
和quitSafely
,退出後 Handler 傳送訊息會失敗。 - 如果手動為子執行緒建立了 Looper,那麼所有事情完成後應該呼叫
quit
方法來終止訊息迴圈,否則執行緒會一直處於等待狀態。
Handler 的工作原理
- Handler 的工作主要包括訊息的傳送(
post
和send
)和接收過程。 -
Handler 傳送訊息的過程僅僅是向訊息佇列中插入了一條訊息,MessageQueue 的
next
方法就會返回這條訊息給 Looper,Looper 收到訊息後就開始處理了,最終訊息由 Looper 交由 Handler 處理,即 Handler 的dispatchMessage
的方法會被回撥。public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }