Android面試必問handler機(jī)制淺析

Handler機(jī)制

1、Handler是什么

Handler是Android中的異步消息處理機(jī)制。當(dāng)發(fā)送一個(gè)消息之后,這個(gè)消息是進(jìn)入一個(gè)消息隊(duì)列(MessageQueue),在消息隊(duì)列中通過Looper去循環(huán)的獲取隊(duì)列中的消息,然后將消息分派給對(duì)應(yīng)的處理者進(jìn)行處理。

2、Handler四模塊

Message:存儲(chǔ)需要處理操作的信息

MessageQueue:先進(jìn)先出,存儲(chǔ)handler發(fā)送過來的消息

Looper:循環(huán)器,它是消息隊(duì)列和handler的通信媒介,1:循環(huán)的取出消息隊(duì)列中的消息;2:將取出的消息發(fā)送給對(duì)應(yīng)的處理者

Handler:主線程和子線程的通信媒介,1:添加消息到消息隊(duì)列; 2:處理循環(huán)器分派過來的消息

3、Handler 死循環(huán)導(dǎo)致應(yīng)用卡死?

在handler機(jī)制中,Looper.loop方法會(huì)不斷循環(huán)獲取Message, 其中的消息的獲取是通過調(diào)用MessageQueue的next()方法獲取的,而該方法會(huì)調(diào)用nativePollOnce()方法 ,這是一個(gè)native方法。底層的實(shí)現(xiàn)涉及到Linux pipe/epoll機(jī)制,nativePollOnce()被阻塞時(shí),主線程會(huì)釋放CPU資源,進(jìn)入休眠狀態(tài). 直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,會(huì)通過pipe管道寫端寫入數(shù)據(jù)來喚醒looper工作。

Android6.0及以前的版本使用管道與epoll來完成Looper的休眠與喚醒的

Android6.0及以后的版本使用eventfd與epoll來完成Looper的休眠與喚醒的

4、子線程中維護(hù)的Looper,如何終止消息循環(huán)?有什么用?

如果不處理的話,會(huì)阻塞線程,處理方案是調(diào)用Looper的quit()(清空所有的延遲和非延遲的消息)和quitSafely()(只清空延遲消息); 這個(gè)方法會(huì)調(diào)用MessageQueue的quit()方法,清空所有的Message,并調(diào)用nativeWake()方法喚醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循環(huán)繼續(xù)執(zhí)行,接下來發(fā)現(xiàn)Message為null后就會(huì)結(jié)束循環(huán),Looper結(jié)束。如此便可以釋放內(nèi)存和線程

5、Handler為何使用管道?

同進(jìn)程線程間內(nèi)存共享,通過handler通信,消息的內(nèi)容是不需要從一個(gè)線程拷貝到另一個(gè)線程,因?yàn)閮蓚€(gè)線程間可使用的內(nèi)存是同一個(gè)區(qū)域。(注意:線程私有區(qū)域ThreadLocal)

管道的作用就是當(dāng)一個(gè)線程準(zhǔn)備好Message,并放入消息池,這時(shí)需要通知了一個(gè)線程B去處理這個(gè)消息。線程A向管道的寫端寫入數(shù)據(jù),管道有數(shù)據(jù)便會(huì)喚醒線程B去處理消息。管道的作用是用于通知另一個(gè)線程的,這便是最核心的作用。

image

6、Handler為何使用管道而非binder

從內(nèi)存角度,通信過程中binder涉及到一次內(nèi)存拷貝,handler機(jī)制中的Message根本不需要拷貝,本身就是在同一片內(nèi)存。

從CPU角度,為了Binder通信底層驅(qū)動(dòng)還需要?jiǎng)?chuàng)建一個(gè)binder線程池,每次通信涉及binder線程的創(chuàng)建和內(nèi)存的分配等比較浪費(fèi)CPU資源

7、handler導(dǎo)致內(nèi)存泄漏的原因

原因:handler發(fā)送的消息在當(dāng)前handler的消息隊(duì)列中,如果此時(shí)activity被finish掉了,那么消息隊(duì)列的消息依舊由handler進(jìn)行處理,若此時(shí)handler申明為內(nèi)存類(非靜態(tài)內(nèi)部類),內(nèi)部類持有外部類的實(shí)例引用,這樣在GC垃圾回收時(shí)發(fā)現(xiàn)Activity還有其他引用存在,因而就不會(huì)去回首這個(gè)Activity,進(jìn)而導(dǎo)致Activity泄漏。

方法:使用靜態(tài)內(nèi)部類,并且使用WeakReference包裹外部類的對(duì)象。首先靜態(tài)內(nèi)部類不持有外部類的引用,使用靜態(tài)的handler不會(huì)導(dǎo)致activity的泄漏,handler定義static的同時(shí),還要用WeakReference包裹外部類的對(duì)象。

最后

相關(guān)推薦

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

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

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