Android 04--Handler與HandlerThread

? ? Handler是非常頻繁使用于各種通信的一種工具,HandlerThread則是比較少用,但是二者之間具有一定的聯系,今天來整理一下他們的大概知識點?,F在我們先看看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部分HandlerThread機制篇

鏈接:https://blog.csdn.net/ClAndEllen/article/details/79346492

---------------------

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容