Android 04--Handler與HandlerThread
Handler是非常頻繁使用於各種通訊的一種工具,HandlerThread則是比較少用,但是二者之間具有一定的聯絡,今天來整理一下他們的大概知識點。現在我們先看看Handlder,瞭解之後再學習HandlerThread。

Handler知識結構圖

HandlerThread知識結構圖
目錄大概如下:
1.Handler是什麼
2.Handler的常規使用
3.Handler的機制
4.Handler的記憶體洩漏與解決方案
5.使用Handler讓子執行緒與子執行緒相互通訊
6.Handler的原始碼解析簡要
7.HandlerThread是什麼
8.HandlerThread的使用方法
9.HandlerThread的機制原理
從以上內容,介紹Handler與HandlerThread的基本知識。
1.Handler是什麼
Handler是用於訊息傳送的一種非同步機制工具,可以收放與處理Message、通過Runnable物件實現關聯相關執行緒的MessageQueue。總的來說常用的功能有:
(1)可以讓對應的Message、Runnable在某個時間點得到處理
(2)可以將耗時的操作放在子執行緒,然後傳送接收訊息,通知主執行緒進行UI更新
2.Handler的常規使用
Handler():預設建構函式將此處理程式與Looper用於當前執行緒( Looper.myLooper, 因此如果是子執行緒,則需要在其之前Looper.prepare,為當前執行緒新建looper,在其構造初始化完成後啟動迴圈,Looper.loop,詳見參考書籍二第212頁)。
Handler(Handler.Callback callback):建構函式將此處理程式與Looper對於當前執行緒,並接受一個回撥介面,您可以在其中處理訊息。
Handler(Looper looper):使用所提供的Looper而不是預設的。
Handler(Looper looper, Handler.Callback callback):使用所提供的Looper而不是預設的,而是接受一個回撥介面來處理訊息。
接下來我們就來看看Handler提供的各種方法吧:
post(Runnable r):導致將Runnable r新增到訊息佇列中。
postAtTime(Runnable r, long uptimeMillis):使Runnable r新增到訊息佇列,並在uptimeMillis.
postDelayed(Runnable r, long delayMillis):使Runnable r被新增到訊息佇列,並在指定的時間流逝後執行。
removeCallbacks(Runnable r):刪除訊息佇列中所有可執行的Runnable訊息任務。
removeMessages(int what):刪除訊息佇列中訊息物件what欄位為"what"的訊息任務。
sendEmptyMessage(int what):傳送一個空訊息物件,並設定這個空訊息的what值。
sendEmptyMessageAtTime(int what, long uptimeMillis):傳送只包含要在特定時間傳遞的值的訊息。
sendEmptyMessageDelayed(int what, long delayMillis):傳送一條訊息,該訊息只包含要在指定的時間間隔後傳遞的值。
sendMessage(Message msg):將訊息推送到訊息佇列的末尾,在當前時間之前完成所有掛起的訊息。
sendMessageAtTime(Message msg, long uptimeMillis):在所有掛起的訊息在絕對時間之前uptimeMillis(以毫秒為單位)之前,將訊息放入訊息佇列中。
sendMessageDelayed(Message msg, long delayMillis):在所有掛起的訊息之前(當前時間+delayMillis)之後,將訊息放入訊息佇列中。
3.Handler的機制
Handler機制是一個非同步訊息機制,由Handler、Message、MessageQueue、Looper四部分所組成,下面我們著重介紹一下這四個的介紹:
(1)Handler:用於傳送、接受、處理訊息的處理者角色。詳細地說就是用於在子執行緒傳送訊息物件Message,在UI執行緒處理訊息物件Message,在子執行緒呼叫sendMessage方法傳送訊息物件Message,而傳送的訊息經過一系列地輾轉之後最終會被傳遞到Handler的handleMessage方法中,最終在handleMessage方法中訊息物件Message被處理;
(2)Message:訊息,就是傳遞的資料封裝物件,也可視為資料的載體;
(3)MessageQueue:訊息的佇列,用於存放Handler所傳送過來的訊息,這些訊息則在此等待被處理。 每個執行緒中只會有一個MessageQueue物件 ,請牢記這句話。其實從字面上就可以看出,MessageQueue底層資料結構是佇列,而且這個佇列只存放Message物件;
(4)Looper:MessageQueue的管家,也就是訊息佇列的管理者, 因為每個執行緒只有一個訊息佇列,因此每個執行緒也只有一個looper 。一般通過Looper.prepare進行原執行緒looper的新建初始化,在呼叫Looper.loop()後則開啟迴圈,從MessageQueue獲取訊息Message,然後獲取其對應的handler,回撥handler.dispatchMessage,進行訊息傳遞與喚起訊息處理(Handler物件的handleMessage方法),直到MessageQueue裡的訊息被全部取出跳出loop裡面的迴圈;
在這裡補充一下,因為每個執行緒只能有一個Looper,也因此只能有一個MessageQueue。但是為什麼每個執行緒只能有一個Looper呢?因為在原始碼裡面,是通過靜態物件sThreadLocal進行Looper的儲存與集合管理(或者說用來儲存Looper 物件),就相當於一個Map集合,鍵位當前的Thead執行緒,值為Looper物件。原始碼如下:
public final class Looper{
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
...
}
從以上四個部分的角色介紹,我們再描述一下Handler的最常用場景進行描述一下:在UI執行緒進行handler的初始化,無論是匿名內部類還是其對應靜態內部類、子類等都需要重寫handleMessage方法,作為訊息回撥處理,通過引數msg來寫接受訊息過後UIi執行緒的邏輯處理的場所。之後,我們獲取主執行緒的handler物件,傳遞給我們的子執行緒,在子執行緒內部進行資料獲取/處理等耗時操作回撥,將回應資料進行封裝成msg,handler.sendMessage進行msg傳送資料。至此,這是上層應用開發者所必須知曉的流程,接下來的步驟就是內部機制實現了;
在sendMessage呼叫之後,msg則存放到對應looper的訊息佇列messageQueue中,等待被處理。在loop 啟用後,則進行對訊息佇列迴圈獲取訊息msg,然後獲取對應msg對應的handler,通過回撥handler的dispatchMessage方法( 分發msg )將訊息傳遞給Handler的handleMessage方法。
自此,整個handler的機制大致完成,可見下圖:

handler機制簡圖
4.handler的記憶體洩漏
原因:非靜態內部類持有外部類的匿名引用,導致外部activity無法得到釋放。
解決方法:handler內部持有外部的弱引用,並把handler改為靜態內部類,在activity的onDestory()中呼叫handler的removeCallback()方法。
5.使用Handler讓子執行緒與子執行緒相互通訊
在handler初始化,之前進行該執行緒looper的新建與初始化Looper.prepare,之後進行呼叫loop。詳細見參考博文
6.Handler的原始碼解析簡要
https://blog.csdn.net/u012827296/article/details/51236614
7.HandlerThread是什麼
先說一下需求,由於多次新建和關閉子執行緒都十分耗費資源,因此推出HandlerThread機制,來對應這類場景。本質上來說,Handler + Thread + Looper,是一個Thread內部有Looper,其繼承於Thread。細化來看就是:
(1)HandlerThread是繼承於Thread,是一個執行緒類;
(2)HandlerThread內部具有Looper物件,可以在自己內部進行loop迴圈;
(3)通過Looper物件將訊息傳遞給內部的Handler物件,可在handleMessage方法裡面執行非同步任務;
(4)它的優點——不會阻塞,對效能消耗比較小;缺點——不能進行多工的執行,需要等待處理,效率較低;
(5)與多執行緒併發機制不同,它是(佇列)序列方式,HandlerThread只有一個執行緒。
8.HandlerThread的使用方法
HandlerThread最明顯的就是支援了主執行緒對子執行緒的通訊。其完整的通訊過程應該是:主執行緒通知子執行緒,再由子執行緒通知主執行緒。對比來看傳統的方式就是:在任務明確後進行子執行緒建立,然後子執行緒將結果通知給主執行緒。這樣的好處就是,主執行緒可以告知子執行緒需要幹什麼,而且可以隨時隨地地進行訊息通知,子執行緒在處理完對應任務後再通知主執行緒。當然,這樣就不可避免了,你主執行緒本身還是需要一個Handler進行訊息回撥而更新UI。
詳細樣例可見參考博文。
9.HandlerThread的機制原理
HandlerThread在內部擁有一個Looper,並在run方法中進行建立,然後放入ThreadLocal裡面,在呼叫loop方法喚起迴圈。Looper.loop()方法會不斷迴圈從MessageQueue中取出訊息處理訊息,沒有訊息是則會阻塞。getLooper()方法是獲取執行緒中的Looper物件,可以用來初始化Handler物件。
這裡說一下 HandlerThread類中的quit、quitSafely方法區別,quit會清空佇列裡的訊息(無論延時或者非延時),而quitSafely則只清空訊息佇列裡的延時訊息。當呼叫qiut或者quitSafely,Looper物件都不會再接收訊息了,也就是說就停止訊息的迴圈了。這時候再呼叫sendMessage或者post,都會返回false,執行緒也隨即停止。
---------------------
參考書籍:《Android開發藝術探索》、《瘋狂Android講義(第3版)》
參考博文:
作者:ClAndEllen 來源:CSDN 題目:Android面試系列文章2018之Android部分Binder機制篇
連結: ofollow,noindex">https://blog.csdn.net/ClAndEllen/article/details/79346492
---------------------