1. 程式人生 > >【Android】簡單全面的理解Handler機制

【Android】簡單全面的理解Handler機制

前言:Handler機制應該是網上講解最多的一種機制(沒有之一),本篇用通俗易懂的語言來介紹一下Handler機制,讓大家可以更好的理解。

什麼是Handler機制?

Handler機制是AndroidSDK提供的一個非常重要的處理非同步訊息的機制,主要是由Handler、Looper、Message和MessageQueue組成,Handler只是訊息處理機制的一部分。
- Message:訊息(分為硬體產生的訊息和軟體產生的訊息)。
- MessageQueue:訊息佇列,主要是向訊息池投遞訊息(MessageQueue.enqueueMessage)和取走訊息池中的訊息(MessageQueue.next)。
- Handler:主要功能是向訊息池傳送訊息(Handler.sendMessage)和處理訊息(Handler.handleMessage)。
- Looper:不停的迴圈執行(Looper.loop),從MessageQueue中取出Message併發送給Handler。

分析上述各部分:

Message:什麼是硬體訊息和軟體訊息呢?硬體訊息就是我們滑動觸控點選按鈕等等,軟體訊息就是我們主動new Message傳送出去的。Message實現了Parcelable介面封裝訊息資料,所以他是存在於記憶體中的。一個實體(類)如果需要封裝到訊息中去就必須實現這一介面。
MessageQueue:相當於一個容器,訊息池。上述看到了.next應該猜到是連結串列形式,實際上確實是單鏈表維護,在插入和刪除上有優勢。在其next()中會無限迴圈,不斷的判斷是否有訊息,有就返回這條訊息並移除。
Looper:Looper建立的時候會建立一個MessageQueue,它們兩個是一一對應的關係。呼叫Looper.loop()的時候訊息迴圈開始,不斷地呼叫MessageQueue的next()方法,當有訊息就處理,否則就堵塞在next()方法中。loop()跟MessageQueue的next()一樣都是死迴圈(原始碼可見for(;;))。退出時呼叫Looper.quit(),它會呼叫MessageQueue的quit()方法,此時next會返回null,然後loop()方法也跟著退出。
Handler

:在主執行緒構造Handler,new Handler()裡呼叫了Looper.myLooper()這個方法,這個方法是獲取當前執行緒的Looper的。在其他執行緒呼叫sendMessage()時主執行緒的MessageQueue會插入一條Message,然後被Looper使用,在Looper的loop()中通過回撥 msg.target.dispatchMessage(msg);傳送給Handler。Handler跟Looper的關係是多對一。

一段Looper的原始碼片段:

**解釋完各部分的分工那來總結一下他們之間的關係:**Handler負責傳送訊息(Message)到MessageQueue中,Looper負責迴圈的接收MessageQueue中的訊息通過回撥方法返還給Handler自己本身。
Handler機制流程圖

容易忽略的Message,使用時應注意的地方有哪些?

Message在Handler機制中看似很不起眼,但至關重要。它封裝了任務攜帶的資訊和該任務的handler,使用時應注意:
- Message可以通過 new 來獲取,但通常使用Message.obtain()或Handler.obtainMessage()方法來從訊息池中獲取空訊息物件,可以節省資源!
- 如果Message只需要攜帶簡單的int型資料,優先使用arg1和arg2來傳遞資料,比Bundle節省記憶體。
- 使用Message.what來標識資訊便於處理Message。
- 最後如果需要從工作執行緒返回很多資料資訊再借助Bundle物件將資料集中到一起放在obj屬性中,返回到主執行緒處理。

Looper原始碼簡述:

上面說了那麼多Looper的方法,但在Activity中使用Handler時沒看到過Looper的影子啊,原來Activity內部包含了一個Looper物件,它會自動管理Looper並處理子執行緒中傳送過來的訊息。前面說到過,初始化Handler時在Handler的建構函式中會把當前執行緒的Looper與Handler關聯,所以在Activity中無需顯式的使用Looper。但在子執行緒中則需要我們自己維護Looper。

原始碼簡述

網上介紹Handler機制的文章解釋最詳細的就是Looper,會附帶原始碼每一篇都會解釋的很透徹。這裡就簡單的總結一下Looper,詳細請去查閱Looper原始碼。可以看出Prepare()的工作方式核心就是將Looperd物件定義為ThreadLocal,將唯一的Looper物件新增到ThreadLocal中。Looper在構造方法中建立了一個MessageQueue和當前執行緒Thread。
那什麼是TheadLocal呢?
ThrealLocal 是一個泛型類。
工作原理:ThreadLocal儲存資料時先獲取當前執行緒,然後通過當前執行緒來建立Values,如果沒有Values就new一個Values例項。然後儲存傳進來的Value。獲取的時候也是根據當前執行緒的Values來獲取的。
它的get()和set()方法如下圖:使用場景:資料是以執行緒為作用域並且不同執行緒具有不同的資料副本時可以使用。

最後說一下為什麼Handler機制會造成記憶體洩露

因為非靜態內部類導致的!
我們知道非靜態內部類預設就會持有外部類的引用,當非靜態內部類物件的生命週期比外部類還長就會導致記憶體洩露。
上面介紹Message時說過mHandler會作為成員變數儲存在傳送的訊息msg中,所以msg就會持有mHandler的引用,而mHandle是MainActivity的非靜態內部類例項,所以mHandler就持有MainActivity的引用。msg間接持有MainActivity的引用。msg傳送到訊息佇列(MessageQueue)中等待Looper輪詢處理。當MainActivity退出後,msg可能還存在訊息佇列中未處理或正在處理。這樣就會導致MainActivity無法被回收,以致發生MainActivity的記憶體洩露。
解決辦法:使用靜態內部類+弱引用的方式mHandler通過弱引用的方式持有MainActivity,當GC執行垃圾回收時遇到MainActivity就會回收並釋放所佔的記憶體單元,避免記憶體洩露。msg還可能存在MessageQueue中,所以在MainActivity銷燬時將mHandler的回撥和傳送的訊息給移除掉。

結束

到此Handler機制的內容就算講完了,但本篇只是用比較容易理解的方式介紹了一下Handler機制,Handler機制在Android開發中隨處可見也很重要。大家很有必要去深入研究一下,去看一下里面的原始碼或結合一些講解原始碼的文章自己理解一下。希望能幫到大家,有錯的地方請提出。