Android 消息機制 Handler MessageQueue Looper

先言

爲了小夥伴們考慮,我先一句話總結一下這個 Android 系統的消息機制 (想更深入的瞭解建議去讀源碼)
Android 消息機制是 Handler 機制 Handler 底層需要 MessageQueue 和 Looper 支持,MessageQueue 用於對消息的存和取,而不取出來消息,Looper 去查找消息,然後再交給對應的Handler 去處理,說白了就是自己發的消息自己處理,Handler 和 Looper 所在的線程相關。

Handler

因爲開發中接觸 Handler 較多先從他講起
下面是一段樸實無華的代碼
在這裏插入圖片描述
代碼第11行,Handler構造
在這裏插入圖片描述
在這裏插入圖片描述
繼續看構造,看到了自己在子線程創建Handler 的報錯了吧,如果沒有Looper 就拋異常啦。
所以構造Handler 之前 必須有 Looper ,調用 Looper.prepare()。小夥伴又有疑問了爲啥主線程構造Looper沒有報錯呢?
源代碼較長分兩段截取
在這裏插入圖片描述
在這裏插入圖片描述
看到沒在程序的入口函數,已經調用了 Looper.prepareMainLooper(); 然後 Looper.loop();
在這裏插入圖片描述
它其實也調用的是 Looper.prepare()

代碼第21行 mHandler.sendMessage(message) 咱們一步一步跟進去
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
關鍵代碼 MessageQueue queue = mQueue;
這個 mQueue 是啥 是Handler構造裏,通過 Looper 拿到的 MessageQueue
在這裏插入圖片描述
這個很關鍵 msg.target = this; 這個msg.target 是啥 就是Handler,因爲 mHandler.sendMessage(message) 最終會調用到 enqueueMessage() 方法,所以this 就是你創建的Handler 這個很關鍵哦。
因爲程序走到這兒了 queue.enqueueMessage(msg, uptimeMillis);所以接下來分析 MessageQueue。

MessageQueue

MessageQueue 消息隊列主要倆操作:插入和讀取。對應的方法就是enqueueMessage() 和 next() 方法。
雖然叫消息隊列 其實內部是一個單鏈表,對插入和刪除上比較有優勢。
源代碼較長分兩段截取
在這裏插入圖片描述
在這裏插入圖片描述
看到是一個單鏈表的插入 就是保存第一條Message 以及判斷時間等。
好,咱們看了存消息,接下來看取消息吧!
老樣子 源代碼較長分兩段截取 有興趣的自己去看源碼,走一遍流程哈!
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
這不用糾結細節,知道是一個無線循環方法,如果沒有消息就一直阻塞在這這裏,有新消息,會返回消息從單鏈表中移除。
好MessageQueue 分析好了,只存和取,但是誰來讓着流程跑起來呢?這就要說到咱們的 Looper

Looper

剛咱們從ActivityThread 的main() 方法中也看到了,Looper.prepareMainLooper(); Looper.loop(); 方法。這就很關鍵了。
Looper.prepareMainLooper() 來開始分析
在這裏插入圖片描述
在這裏插入圖片描述
這兒看到了一個關鍵的類 ThreadLocal 等一會再說 ,看着樣子 就是 存Looper的
這時候 new Looper(quitAllowed) new了一個Looper對象。
去構造看看去
在這裏插入圖片描述
看到沒 一個 MessageQueue 誕生了,同時還有一個 Thread 的 看來 ThreadLocal 不簡單啊,還和線程有關係,莫急咱們稍後再看。
好,分析完 Looper.prepare() 咱們來分下Loop的另一個重要方法 loop()
老樣子 源代碼較長分段截取 有興趣的自己去看源碼 關鍵代碼我已經標紅 咱們只看關鍵的,那些太細節的沒必要,不能沉迷其中無法自拔啊!
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
只是截取了關鍵代碼 ,標紅的就是關鍵代碼。
loop() 中,會拿到MessageQueue 調用他自己的next() 方法 是可能被阻塞的,當next()方法返回null 的時候,可以跳出死循環 Looper 有一個quit() 方法和一個 quitSafely() 方法 ,調用的時候 next() 返回 null 注意 如果自己調用Looper.loop()方法一點要記得退出。
關鍵代碼 msg.target.dispatchMessage(msg);
還記得這個 msg.target 嗎? 這個是咱們 sendMessage()方法時的 那個Handler對象啊,它被存在了MessageQueue中 ,看到沒這就完成了子線程到主線程的切換。應爲Handler 創建的時候是主線程的Looper 創建的,所以切換到指定的線程去啦。

好的,接下來咱們分析 Handler 是如何處理消息的 msg.target.dispatchMessage(msg);
好的 咱們回到 Handler 的源碼
在這裏插入圖片描述
這裏咱們就看到了,重寫次數非常多的 handleMessage(msg);方法啦!
第一個msg.callback 其實就是 Runnable
mCallback 這個東西其實就是構造Handler的時候一個參數如果不爲null 也會走到 handleMessage()方法,爲null 就走到咱們重寫的 handleMessage 方法啦!

在這裏插入圖片描述
來自己處理消息吧!!!
這個就是 Android 消息機制的整個流程,接下來我補充一下 ThreadLocal

ThreadLocal

Handler 內部獲取當前線程的 Looper 就是通過它
在這裏插入圖片描述
從Looper 源碼中我們可以看到 兩個操作 一個是 new 一個 Looper 第二是 把Looper 存在 ThreadLocal中,ThreadLocal 線程單例 用ThreadLocal.set方法把一個對象和一個線程建立起一一對應的關係,而且一個線程對應一個Looper 對應一個MessageQueue。
咱們去看源碼
在這裏插入圖片描述
可以很清晰的看到 把當前的線程和Looper 存起來了
在這裏插入圖片描述
使用一個數組存起來的,具體咋存的,咱們稍微看一個即可。
在這裏插入圖片描述

在這裏插入圖片描述

總結

  • 構造 Handler 會初始化 Looper 主線程不用 ActivityThread 已經初始化好並且 loop() 了。
  • Looper 在 new 的時候會構建 MessageQueue ,同時 Handler 也拿到了其引用
  • Handler 調用 sendMessage方法的時候,其實是調用 MessageQueue 的 enqueueMessag()方法
  • MessageQueue 會通過一個成員變量記住當前的Handler
  • 在Looper loop()的時候會 調用 MessageQueue 的next() 方法
  • 最後 msg.target(就是你自己構建的Handler) 又會處理消息
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章