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

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

什么是Handler機制?

Handler機制是AndroidSDK提供的一個非常重要的處理異步消息的機制,主要是由Handler、Looper、Message和MessageQueue組成,Handler只是消息處理機制的一部分。

  • Message:消息(分為硬件產(chǎn)生的消息和軟件產(chǎn)生的消息)。
  • MessageQueue:消息隊列,主要是向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池中的消息(MessageQueue.next)。
  • Handler:主要功能是向消息池發(fā)送消息(Handler.sendMessage)和處理消息(Handler.handleMessage)。
  • Looper:不停的循環(huán)執(zhí)行(Looper.loop),從MessageQueue中取出Message并發(fā)送給Handler。
分析上述各部分:

Message:什么是硬件消息和軟件消息呢?硬件消息就是我們滑動觸摸點擊按鈕等等,軟件消息就是我們主動new Message發(fā)送出去的。Message實現(xiàn)了Parcelable接口封裝消息數(shù)據(jù),所以他是存在于內(nèi)存中的。一個實體(類)如果需要封裝到消息中去就必須實現(xiàn)這一接口。
MessageQueue:相當(dāng)于一個容器,消息池。上述看到了.next應(yīng)該猜到是鏈表形式,實際上確實是單鏈表維護,在插入和刪除上有優(yōu)勢。在其next()中會無限循環(huán),不斷的判斷是否有消息,有就返回這條消息并移除。
Looper:Looper創(chuàng)建的時候會創(chuàng)建一個MessageQueue,它們兩個是一一對應(yīng)的關(guān)系。調(diào)用Looper.loop()的時候消息循環(huán)開始,不斷地調(diào)用MessageQueue的next()方法,當(dāng)有消息就處理,否則就堵塞在next()方法中。loop()跟MessageQueue的next()一樣都是死循環(huán)(源碼可見for(;;))。退出時調(diào)用Looper.quit(),它會調(diào)用MessageQueue的quit()方法,此時next會返回null,然后loop()方法也跟著退出。
Handler:在主線程構(gòu)造Handler,new Handler()里調(diào)用了Looper.myLooper()這個方法,這個方法是獲取當(dāng)前線程的Looper的。在其他線程調(diào)用sendMessage()時主線程的MessageQueue會插入一條Message,然后被Looper使用,在Looper的loop()中通過回調(diào) msg.target.dispatchMessage(msg);發(fā)送給Handler。Handler跟Looper的關(guān)系是多對一。

一段Looper的源碼片段:

解釋完各部分的分工那來總結(jié)一下他們之間的關(guān)系:Handler負責(zé)發(fā)送消息(Message)到MessageQueue中,Looper負責(zé)循環(huán)的接收MessageQueue中的消息通過回調(diào)方法返還給Handler自己本身。

Handler機制流程圖

容易忽略的Message,使用時應(yīng)注意的地方有哪些?

Message在Handler機制中看似很不起眼,但至關(guān)重要。它封裝了任務(wù)攜帶的信息和該任務(wù)的handler,使用時應(yīng)注意:

  • Message可以通過 new 來獲取,但通常使用Message.obtain()或Handler.obtainMessage()方法來從消息池中獲取空消息對象,可以節(jié)省資源!
  • 如果Message只需要攜帶簡單的int型數(shù)據(jù),優(yōu)先使用arg1和arg2來傳遞數(shù)據(jù),比Bundle節(jié)省內(nèi)存。
  • 使用Message.what來標(biāo)識信息便于處理Message。
  • 最后如果需要從工作線程返回很多數(shù)據(jù)信息再借助Bundle對象將數(shù)據(jù)集中到一起放在obj屬性中,返回到主線程處理。

Looper源碼簡述:

上面說了那么多Looper的方法,但在Activity中使用Handler時沒看到過Looper的影子啊,原來Activity內(nèi)部包含了一個Looper對象,它會自動管理Looper并處理子線程中發(fā)送過來的消息。前面說到過,初始化Handler時在Handler的構(gòu)造函數(shù)中會把當(dāng)前線程的Looper與Handler關(guān)聯(lián),所以在Activity中無需顯式的使用Looper。

但在子線程中則需要我們自己維護Looper。
源碼簡述

網(wǎng)上介紹Handler機制的文章解釋最詳細的就是Looper,會附帶源碼每一篇都會解釋的很透徹。這里就簡單的總結(jié)一下Looper,詳細請去查閱Looper源碼。

可以看出Prepare()的工作方式核心就是將Looperd對象定義為ThreadLocal,將唯一的Looper對象添加到ThreadLocal中。
Looper在構(gòu)造方法中創(chuàng)建了一個MessageQueue和當(dāng)前線程Thread。
那什么是TheadLocal呢?
ThrealLocal 是一個泛型類。
工作原理:ThreadLocal存儲數(shù)據(jù)時先獲取當(dāng)前線程,然后通過當(dāng)前線程來創(chuàng)建Values,如果沒有Values就new一個Values實例。然后存儲傳進來的Value。獲取的時候也是根據(jù)當(dāng)前線程的Values來獲取的。
它的get()和set()方法如下圖:
使用場景:數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本時可以使用。

最后說一下為什么Handler機制會造成內(nèi)存泄露

因為非靜態(tài)內(nèi)部類導(dǎo)致的!
我們知道非靜態(tài)內(nèi)部類默認就會持有外部類的引用,當(dāng)非靜態(tài)內(nèi)部類對象的生命周期比外部類還長就會導(dǎo)致內(nèi)存泄露。


上面介紹Message時說過mHandler會作為成員變量保存在發(fā)送的消息msg中,所以msg就會持有mHandler的引用,而mHandle是MainActivity的非靜態(tài)內(nèi)部類實例,所以mHandler就持有MainActivity的引用。msg間接持有MainActivity的引用。msg發(fā)送到消息隊列(MessageQueue)中等待Looper輪詢處理。當(dāng)MainActivity退出后,msg可能還存在消息隊列中未處理或正在處理。這樣就會導(dǎo)致MainActivity無法被回收,以致發(fā)生MainActivity的內(nèi)存泄露。
解決辦法:使用靜態(tài)內(nèi)部類+弱引用的方式
mHandler通過弱引用的方式持有MainActivity,當(dāng)GC執(zhí)行垃圾回收時遇到MainActivity就會回收并釋放所占的內(nèi)存單元,避免內(nèi)存泄露。msg還可能存在MessageQueue中,所以在MainActivity銷毀時將mHandler的回調(diào)和發(fā)送的消息給移除掉。

結(jié)束

到此Handler機制的內(nèi)容就算講完了,但本篇只是用比較容易理解的方式介紹了一下Handler機制,Handler機制在Android開發(fā)中隨處可見也很重要。大家很有必要去深入研究一下,去看一下里面的源碼或結(jié)合一些講解源碼的文章自己理解一下。希望能幫到大家,有錯的地方請?zhí)岢鰜砉?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容