前言
Handler、Looper以及Message之間的關系,概括性來說,Looper負責的是建立一個MessageQueue對象,然後進入到一個無限循環體中不斷取出消息,而這些消息都是由一個或者多個Handler進行建立處理
Messagequeue 的資料結構是什麼?
基礎資料結構中 “先進先出” 的一種資料結構
Handler post 原理
消息是通過 MessageQueen 中的 enqueueMessage()方法加入消息隊列中的,并且它在放入中就進行好排序,連結清單頭的延遲時間小,尾部延遲時間最大
- Looper.loop()通過 MessageQueue 中的 next()取消息
- next()中如果目前連結清單頭部消息是延遲消息,則根據延遲時間進行消息隊列會 阻塞,不傳回給 Looper message,知道時間到了,傳回給 message
- 如果在阻塞中有新的消息插入到連結清單頭部則喚醒線程
- Looper 将新消息交給回調給 handler 中的 handleMessage 後,繼續調用 MessageQueen的next()方法,如果剛剛的延遲消息還是時間未到,則計算時間繼續阻塞
handler.postDelay()的實作是通過MessageQueue中執行時間順序排列,消息隊列阻塞和喚醒的方式結合實作的;如果真的是通過延遲将消息放入到 MessageQueen中,那放入多個延遲消息就要維護多個定時器
Android 消息機制的簡介
在 Android 中使用消息機制,我們首先想到的就是 Handler;沒錯,Handler是 Android 消息機制的上層接口;Handler 的使用過程很簡單,通過它可以輕松地将一個任務切換到Handler所在的線程中去執行。通常情況下,Handler的使用場景就是更新UI
在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新 UI;這便是消息機制的典型應用場景。我們通常隻會接觸到 Handler 和 Message 來完 成消息機制,其實内部還有兩大助手來共同完成消息傳遞
消息機制的模型 消息機制主要包含: MessageQueue,Handler和Looper這三大部分,以及Message,下面我們一一介紹
- Message: 需要傳遞的消息,可以傳遞資料;
- MessageQueue: 消息隊列,但是它的内部實作并不是用的隊列,實際上是通過 一個單連結清單的資料結構來維護消息清單,因為單連結清單在插入和删除上比較有優 勢。主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
- Handler: 消息輔助類,主要功能向消息池發送各種消息事件 (Handler.sendMessage)和處理相應消息事件(Handler.handleMessage);
- Looper: 不斷循環執行(Looper.loop),從 MessageQueue中讀取消息,按分發機制将消息分發給目标處理者
消息機制的架構
消息機制的運作流程: 在子線程執行完耗時操作,當 Handler 發送消息時,将會 調用MessageQueue.enqueueMessage,向消息隊列中添加消息
當通過Looper.loop 開啟循環後,會不斷地從線程池中讀取消息 ,即調用 MessageQueue.next,然後 調用目标 Handler(即發送該消息的 Handler)的 dispatchMessage 方法傳遞消息,然後傳回到 Handler 所線上程,目标 Handler 收到消息,調用 handleMessage方法,接收消息,處理消息
MessageQueue,Handler 和 Looper 三者之間的關系: 每個線程中隻能存在一個 Looper,Looper 是儲存在 ThreadLocal 中的。主線程(UI 線程)已經建立了一個 Looper,是以在主線程中不需要再建立 Looper,但是在其他線程中需要建立 Looper
每個線程中可以有多個 Handler,即一個Looper 可以處理來自多個 Handler的消息 ;Looper 中維護一個MessageQueue,來維護消息隊列,消息隊列中的 Message可以來自不同的 Handler
- Looper 有一個 MessageQueue 消息隊列
- MessageQueue 有一組待處理的 Message
- Message中記錄發送和處理消息的 Handler
- Handler中有 Looper 和 MessageQueue
我們可以使用 Handler 發送并處理與一個線程關聯的 Message 和 Runnable ;(注意:Runnable 會被封裝進一個 Message,是以它本質上還是一個 Message ) 每個 Handler 都會跟一個線程綁定,并與該線程的 MessageQueue 關聯在一起, 進而實作消息的管理以及線程間通信
- Handler 的背後有着 Looper 以及 MessageQueue 的協助,三者通力合作,分工 明确;嘗試小結一下它們的職責,如下:
- Looper : 負責關聯線程以及消息的分發在該線程下從 MessageQueue 擷取 Message,分發給 Handler ;
- MessageQueue : 是個隊列,負責消息的存儲與管理,負責管理由 Handler 發送過 來的 Message ;
-
Handler : 負責發送并處理消息,面向開發者,提供 API,并隐藏背後實作的細節
Handler 發送的消息由 MessageQueue 存儲管理,并由 Loopler 負責回調消息 到 handleMessage();線程的轉換由 Looper 完成,handleMessage() 所線上程由 Looper.loop() 調用 者所線上程決定
Handler 引起的記憶體洩露原因以及最佳解決方案
Handler 允許我們發送延時消息,如果在延時期間使用者關閉了 Activity,那麼該 Activity 會洩露
這個洩露是因為 Message 會持有 Handler,而又因為 Java 的特性,内部類會 持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導緻 Activity 洩露
解決該問題的最有效的方法是:将 Handler 定義成靜态的内部類,在内部持有 Activity 的弱引用,并及時移除所有消息
示例代碼如下:
private static class SafeHandler extends Handler {
private WeakReference<HandlerActivity> ref;
public SafeHandler(HandlerActivity activity) {
this.ref = new WeakReference(activity);
}
@Override
public void handleMessage(final Message msg) {
HandlerActivity activity = ref.get();
if (activity != null) {activity.handleMessage(msg);
}
}}
并且再在 Activity.onDestroy() 前移除消息,加一層保障:
@Overrideprotected void onDestroy() {
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
這樣雙重保障,就能完全避免記憶體洩露了。注意:單純的在 onDestroy 移除消息并不保險,因為 onDestroy 并不一定執行
-
- Handler 的 背 後 有 Looper 、 MessageQueue 支 撐 , Looper 負 責 消 息 分 發 , MessageQueue 負責消息管理;
-
- 在建立 Handler 之前一定需要先建立 Looper;
-
- Looper 有退出的功能,但是主線程的 Looper 不允許退出;
-
- 異步線程的 Looper 需要自己調用 Looper.myLooper().quit(); 退出;
-
- Runnable 被封裝進了 Message,可以說是一個特殊的 Message;
-
- Handler.handleMessage() 所在的線程是 Looper.loop() 方法被調用的線程,也可以說成 Looper所在的線程,并不是建立Handler的線程;
-
- 使用内部類的方式使用Handler可能會導緻記憶體洩露,即便在 Activity.onDestroy 裡移除延時消息,必須要寫成靜态内部類
Android UI 是線程不安全的,如果在子線程中嘗試進行 UI 操作,程式就有可能 會崩潰
相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也 是早已爛熟于心,即建立一個 Message 對象,然後借助 Handler 發送出去,之 後在 Handler 的 handleMessage()方法中獲得剛才發送的 Message 對象,然 後在這裡進行 UI 操作就不會再出現崩潰了
在主線程中可 以直接建立 Handler 對象,而在子線程中需要先調用 Looper.prepare()才能創 建 Handler 對象
有關 Handler 的知識點就到這裡了, 有需要學習更多關于 Handle 知識點的同學,可以 私信 發送 “進階” 即可 擷取一份 學習筆記 ,相信能夠幫助大家 查漏補缺
資料部分内容展示如下:
《Handler 機制之 Thread》
- 線程概念
- Android線程的實作
- 線程的阻塞
- 關于線程上下文切換
- 關于線程的安全問題
- 守護線程
- 線程的記憶體
《Handler機制之ThreadLocal》
- Java中的ThreadLocal
- ThreadLocal的前世今生
- Android中的ThreadLocal
- Android 面試中的關于ThreadLocal的問題
- ThreadLocal的結構
- ThreadLocal靜态類ThreadLocal.Values
- ThreadLocal的總結
完整版PDF文檔擷取方式: 私信 發送 “進階” 即可 免費擷取
對于程式員來說,要學習的知識内容、技術有太多太多,要想不被環境淘汰就隻有不斷提升自己,從來都是我們去适應環境,而不是環境來适應我們
技術是無止境的,你需要對自己送出的每一行代碼、使用的每一個工具負責,不斷挖掘其底層原理,才能使自己的技術升華到更高的層面
Android 架構師之路還很漫長,與君共勉