面試問(wèn)題
- 什么是Handler
- Handler的組成部分
- 一個(gè)線(xiàn)程有幾個(gè)Handler双饥?
- 一個(gè)線(xiàn)程有幾個(gè)Looper损姜?如何保證洋机?
- Handler內(nèi)存泄漏的原因?為什么其它的內(nèi)部類(lèi)沒(méi)有說(shuō)過(guò)這個(gè)問(wèn)題曾掂?
- 如果想在子線(xiàn)程new Handler要做哪些準(zhǔn)備?
- 子線(xiàn)程中維護(hù)的Looper壁顶,消息隊(duì)列無(wú)消息的時(shí)候的處理方案是什么珠洗?有什么用?
- 既然可以存在多個(gè)Handler往MessageQueue中添加數(shù)據(jù)(發(fā)消息Handler可能在不同線(xiàn)程)若专,那它內(nèi)部是如何保證線(xiàn)程安全的许蓖?
- 我們使用Message時(shí)應(yīng)該如何創(chuàng)建它?
- Looper死循環(huán)為什么不會(huì)導(dǎo)致程序卡死调衰?
什么是Handler
Handler就是解決線(xiàn)程與線(xiàn)程間的通信膊爪。
當(dāng)我們?cè)谧泳€(xiàn)程處理耗時(shí)操作,耗時(shí)操作完成后我們需要更新UI的時(shí)候嚎莉,這就是需要使用Handler來(lái)處理了米酬,因?yàn)樽泳€(xiàn)程不能更 新UI,Handler能讓我們?nèi)菀椎陌讶蝿?wù)切換回來(lái)它所在的線(xiàn)程趋箩。
消息處理機(jī)制本質(zhì):一個(gè)線(xiàn)程開(kāi)啟循環(huán)模式持續(xù)監(jiān)聽(tīng)并依次處理其他線(xiàn)程給它發(fā)的消息赃额。
Handler的組成部分
- Message:message就是用來(lái)線(xiàn)程直接的交互數(shù)據(jù)琼懊,可以攜帶少量數(shù)據(jù),它有四個(gè)常用的字段 1.what 2.age1 3.age2, 4.obj
what爬早,age1哼丈,age2可以攜帶整行數(shù)據(jù),obj呢就可以是任意類(lèi)型筛严。 - Handler:handler呢就是用來(lái)發(fā)送消息和處理消息的醉旦,發(fā)送消息我們一般使用的都是Send和Post方法這個(gè)系列的方法,而這個(gè)Post一系列方法是通過(guò)Send一系列方法來(lái)實(shí)現(xiàn)的桨啃,而send的這些方法最后都是通過(guò)SendMessageAtTime()方法來(lái)實(shí)現(xiàn)的车胡,Handler發(fā)送一條消息,就在消息隊(duì)列中插一條消息照瘾。
- Message Queue:消息隊(duì)列的意思匈棘,它主要用于存放所有通過(guò)Handler發(fā)送的消息,這部分消息會(huì)一直存儲(chǔ)在消息隊(duì)列中析命,等待被處理主卫,每個(gè)線(xiàn)程里面只能有一個(gè)Message Queue對(duì)象。
MessageQueue的鹃愤,它的底層是通過(guò)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表的簇搅,它有兩個(gè)方法 enQueueMessage()和next()
enQueueMessage()主要根據(jù)時(shí)間的順序向單鏈表中插入一條消息,next方法是一個(gè)無(wú)限循環(huán)的方法软吐,如果有消息返回這條消息瘩将,并從鏈表中移除,如果沒(méi)有消息的話(huà)凹耙,就一直阻塞在這里 - Looper:每個(gè)線(xiàn)程通過(guò)Handler發(fā)送的消息都保存在MessageQueue中姿现,Looper通過(guò)Loop()的方法就去進(jìn)入一個(gè)無(wú)限循環(huán)當(dāng)中,然后每當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息肖抱,就會(huì)將它取出來(lái)备典,并傳遞到Handler的handlermessage()方法中,每個(gè)線(xiàn)程只能有一個(gè)Looper對(duì)象虐沥。
Looper在這個(gè)循環(huán)中不斷的從messageQueue的next方法中獲取消息熊经,而next方法是一個(gè)阻塞操作,沒(méi)有消息的時(shí)候一直處于阻塞狀態(tài)欲险,當(dāng)有消息了通過(guò)dspatchMessage()發(fā)送消息給Handler對(duì)象 - Theradlcal:messageQueue對(duì)象和Looper對(duì)象在每個(gè)線(xiàn)程中都只會(huì)有一個(gè)對(duì)象镐依,怎么來(lái)保證它有一個(gè)對(duì)象,就通過(guò)Threadlocal來(lái)保存天试,Threadlocal是一個(gè)線(xiàn)程內(nèi)部的數(shù)據(jù)存儲(chǔ)類(lèi)槐壳,通過(guò)它可以在指定的線(xiàn)程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后只能在指定線(xiàn)程中可以獲取到存儲(chǔ)的數(shù)據(jù)喜每,對(duì)于其他的線(xiàn)程來(lái)說(shuō)則無(wú)法獲取到數(shù)據(jù)务唐。
一個(gè)線(xiàn)程有幾個(gè)Handler
一個(gè)線(xiàn)程可以有多個(gè)Handler雳攘,通過(guò)new Handler的方式創(chuàng)建。
一個(gè)線(xiàn)程有幾個(gè)Looper枫笛,如何保證
一個(gè)線(xiàn)程只能有一個(gè)Looper吨灭,通過(guò)Looper.perpare方法會(huì)創(chuàng)建一個(gè)Looper保存在ThreadLocal中,每個(gè)線(xiàn)程都有一個(gè)LocalThreadMap刑巧,會(huì)將Looper保存在對(duì)應(yīng)線(xiàn)程中的LocalThreadMap喧兄,key為T(mén)hreadLocal,value為L(zhǎng)ooper啊楚。
Handler內(nèi)存泄漏的原因吠冤,為什么其它的內(nèi)部類(lèi)沒(méi)有說(shuō)過(guò)這個(gè)問(wèn)題
內(nèi)部類(lèi)持有外部類(lèi)的對(duì)象,handler持有activity的對(duì)象恭理,當(dāng)頁(yè)面activity關(guān)閉時(shí)拯辙,handler還在發(fā)送消息,handler持有activity的對(duì)象颜价,導(dǎo)致handler不能及時(shí)被回收涯保,所以造成內(nèi)存泄漏。
因?yàn)楫?dāng)handler發(fā)送消息時(shí)拍嵌,會(huì)有耗時(shí)操作遭赂,并且會(huì)利用線(xiàn)程中的looper和messageQueue進(jìn)行消息發(fā)送,looper和messageQueue的生命周期是很長(zhǎng)的横辆,和application一樣,所以handler不容易被銷(xiāo)毀茄猫,所以造成內(nèi)存泄漏狈蚤。
解決方案有:
- 把handler生命成靜態(tài)內(nèi)部類(lèi),靜態(tài)內(nèi)部類(lèi)不會(huì)持有activity划纽,所以不會(huì)造成內(nèi)存泄漏脆侮,
- 弱引用(WeakReference):把使用handle的activity設(shè)置成弱引用,
如果想在子線(xiàn)程new Handler要做哪些準(zhǔn)備
可以在子線(xiàn)程中創(chuàng)建Handler勇劣,我們需要調(diào)用Looper.perpare和Looper.loop方法靖避。或者通過(guò)獲取主線(xiàn)程的looper來(lái)創(chuàng)建Handler比默。
子線(xiàn)程中維護(hù)的Looper幻捏,消息隊(duì)列無(wú)消息的時(shí)候的處理方案是什么
應(yīng)該調(diào)用Looper的quit方法,因?yàn)榭梢詫ooper中的messageQueue里的message都移除掉命咐,并且將內(nèi)存釋放篡九。
Handler如何保證線(xiàn)程安全
通過(guò)synchronized鎖機(jī)制保證線(xiàn)程安全。
我們使用Message時(shí)應(yīng)該如何創(chuàng)建它
Message.obtain來(lái)創(chuàng)建Message醋奠。這樣會(huì)復(fù)用之前的Message的內(nèi)存榛臼,不會(huì)頻繁的創(chuàng)建對(duì)象伊佃,導(dǎo)致內(nèi)存抖動(dòng)。
Looper死循環(huán)為什么不會(huì)導(dǎo)致程序卡死
- 導(dǎo)致應(yīng)用卡死的原因只有一下幾種情況:
- 輸入事件在5秒內(nèi)未響應(yīng)沛善;
- 在主線(xiàn)程執(zhí)行耗時(shí)操作航揉;
- Looper.loop()函數(shù)會(huì)在死循環(huán)里不斷的去獲取messagequeue里的message,當(dāng)消息隊(duì)列里沒(méi)有消息的時(shí)候金刁,looper會(huì)進(jìn)入休眠階段迷捧。Looper為了防止message執(zhí)行過(guò)于耗時(shí)的操作,導(dǎo)致隊(duì)列阻塞胀葱,就給message設(shè)置一個(gè)ANR的狀態(tài)漠秋。可見(jiàn)抵屿,looper的死循環(huán)和應(yīng)用卡死是兩個(gè)不同的概念庆锦。
點(diǎn)擊頁(yè)面上的按鈕后更新TextView的內(nèi)容,談?wù)勀愕睦斫?/h3>
點(diǎn)擊按鈕的時(shí)候會(huì)發(fā)送消息到Handler轧葛,但是為了保證優(yōu)先執(zhí)行搂抒,會(huì)加一個(gè)標(biāo)記異步,同時(shí)會(huì)發(fā)送一個(gè)target為null的消息尿扯,這樣在使用消息隊(duì)列的next獲取消息的時(shí)候求晶,如果發(fā)現(xiàn)消息的target為null,那么會(huì)遍歷消息隊(duì)列將有異步標(biāo)記的消息獲取出來(lái)優(yōu)先執(zhí)行衷笋,執(zhí)行完之后會(huì)將target為null的消息移除芳杏。(同步屏障)