Handler機(jī)制常見(jiàn)問(wèn)題

概念:

MessageQueue是一個(gè)優(yōu)先級(jí)隊(duì)列,messge中的when字段有其執(zhí)行的時(shí)間挡爵,新加入的message會(huì)與已有的消息比較時(shí)間南誊,較早的在前面。
Message是消息實(shí)體锭环,通常需要用到what和obj字段傳遞消息,內(nèi)部還有when字段用于存儲(chǔ)消息執(zhí)行的時(shí)間泊藕,target用來(lái)存儲(chǔ)發(fā)送Message的Handler對(duì)象正勒。
Looper是輪詢(xún)器。
Handler是消息發(fā)送器穴亏。

流程:

Handler通過(guò)sendMessage()將Message發(fā)送到MessageQueue中挠乳,這是存消息的過(guò)程;
Looper通過(guò)調(diào)用MessageQueue的next()方法取出Message對(duì)象讼呢,由于這是一個(gè)時(shí)間優(yōu)先級(jí)的隊(duì)列撩鹿,所以會(huì)取到隊(duì)頭的Message,然后拿當(dāng)前時(shí)間去跟它的執(zhí)行時(shí)間悦屏,也就是when參數(shù)去對(duì)比节沦,如果時(shí)間還沒(méi)到,就先不會(huì)執(zhí)行础爬;

1甫贯、一個(gè)線程有幾個(gè)Handler?

多個(gè)看蚜,通常我們開(kāi)發(fā)過(guò)程中就會(huì)new出不止一個(gè)Handler叫搁。

2、一個(gè)線程有幾個(gè)Looper失乾?如何保證常熙?

1個(gè)。
Looper的構(gòu)造是私有的碱茁,只有通過(guò)其prepare()方法構(gòu)建出來(lái)裸卫,當(dāng)調(diào)用了Looper的prepare()方法后,會(huì)調(diào)用ThreadLocal中的get()方法檢查T(mén)hreadLocalMap中是否已經(jīng)set過(guò)Looper纽竣,如果有墓贿,則會(huì)拋出異常茧泪,提示每個(gè)線程只能有一個(gè)Looper,如果沒(méi)有聋袋,則會(huì)往ThreadLocalMap中set一個(gè)new出來(lái)的Looper對(duì)象队伟。這樣可以保證ThreadLocalMap和Looper一一對(duì)應(yīng),即一個(gè)ThreadLocalMap只會(huì)對(duì)應(yīng)一個(gè)Looper幽勒。而這里的ThreadLocalMap是在Thread中的一個(gè)全局變量嗜侮,也只會(huì)有一個(gè),所以就可以保證一個(gè)Thread中只有一個(gè)Looper啥容。

3锈颗、Handler內(nèi)存泄漏的原因?為什么其他的內(nèi)部類(lèi)沒(méi)有說(shuō)過(guò)有這個(gè)問(wèn)題咪惠?

內(nèi)部類(lèi)持有外部的引用击吱。
Handler原理:由于Handler可以發(fā)送延遲消息,所以為了保證消息執(zhí)行完畢后遥昧,由同一個(gè)Handler接收到覆醇,所以發(fā)送出去的Message中會(huì)持有Handler的引用,這個(gè)引用存在Message的target字段中炭臭,是Handler所有的sendMessage()方法最后都會(huì)調(diào)用enqueueMessage()永脓,而在enqueueMessage()中會(huì)給Message的target字段賦值this。
因此Message持有Handler的引用徽缚,Handler又持有Activity的引用憨奸,所以在Message處理完之前革屠,如果Activity被銷(xiāo)毀了凿试,就會(huì)造成內(nèi)存泄漏。
解決似芝?可以使用static修飾Handler對(duì)象那婉。

4、為何主線程可以new Handler党瓮?如果想要在子線程中new Handler要做些什么準(zhǔn)備详炬?

因?yàn)樵贏ctivityThread中的main()已經(jīng)對(duì)Looper進(jìn)行了prepar()操作,所以可以直接在主線程new Handler寞奸。
如果想在子線程中new Handler呛谜,則需要先手動(dòng)調(diào)用Looper的prepare()方法初始化Looper,再調(diào)用Looper的loop()方法使Looper運(yùn)轉(zhuǎn)枪萄。

5隐岛、子線程中維護(hù)的Looper,消息隊(duì)列無(wú)消息的時(shí)候的處理方案是什么瓷翻?有什么用聚凹?

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

6、既然可以存在多個(gè)Handler往MessageQueue中添加數(shù)據(jù)(發(fā)消息時(shí)各個(gè)Handler可能處于不同線程)愉豺,那它內(nèi)部是如何確保線程安全的篓吁?Handler的delay消息(延遲消息)時(shí)間準(zhǔn)確嗎?

添加消息的方法enqueueMessage()中有synchronize修飾蚪拦,取消息的方法next()中也有synchronize修飾杖剪。
由于上述的加鎖操作,所以時(shí)間不能保證完全準(zhǔn)確驰贷。

7盛嘿、我們使用Message時(shí)應(yīng)該如何創(chuàng)建它?

使用Message的obtain()方法創(chuàng)建括袒,直接new出來(lái)容易造成內(nèi)存抖動(dòng)次兆。
內(nèi)存抖動(dòng)是由于頻繁new對(duì)象,gc頻繁回收導(dǎo)致锹锰,而且由于可能被別的地方持有導(dǎo)致無(wú)法及時(shí)回收所以會(huì)導(dǎo)致內(nèi)存占用越來(lái)越高芥炭。
使用obtain()對(duì)內(nèi)存復(fù)用,可以避免內(nèi)存抖動(dòng)的發(fā)生恃慧。其內(nèi)部維護(hù)了一個(gè)Message池园蝠,其是一個(gè)鏈表結(jié)構(gòu),當(dāng)調(diào)用obtain()的時(shí)候會(huì)復(fù)用表頭的Message痢士,然后會(huì)指向下一個(gè)彪薛。如果表頭沒(méi)有可復(fù)用的message則會(huì)創(chuàng)建一個(gè)新的對(duì)象,這個(gè)對(duì)象池的最大長(zhǎng)度是50怠蹂。

8善延、使用Handler的postDelay后消息隊(duì)列會(huì)有什么變化?

如果此時(shí)消息隊(duì)列為空城侧,不會(huì)執(zhí)行易遣,會(huì)計(jì)算消息需要等待的時(shí)間,等待時(shí)間到了繼續(xù)執(zhí)行赞庶。

9训挡、Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死澳骤?

卡死就是ANR,產(chǎn)生的原因有2個(gè):
1澜薄、在5s內(nèi)沒(méi)有響應(yīng)輸入的事件(例如按鍵为肮,觸摸等),2肤京、BroadcastReceiver在10s內(nèi)沒(méi)有執(zhí)行完畢颊艳。
事實(shí)上我們所有的Activity和Service都是運(yùn)行在loop()函數(shù)中,以消息的方式存在忘分,所以在沒(méi)有消息產(chǎn)生的時(shí)候棋枕,looper會(huì)被block(阻塞),主線程會(huì)進(jìn)入休眠妒峦,一旦有輸入事件或者Looper添加消息的操作后主線程就會(huì)被喚醒重斑,從而對(duì)事件進(jìn)行響應(yīng),所以不會(huì)導(dǎo)致ANR
簡(jiǎn)單來(lái)說(shuō)looper的阻塞表明沒(méi)有事件輸入肯骇,而ANR是由于有事件沒(méi)響應(yīng)導(dǎo)致窥浪,所以looper的死循環(huán)并不會(huì)導(dǎo)致應(yīng)用卡死。

其他問(wèn)題:

1笛丙、兩個(gè)時(shí)間相等的消息怎么處理漾脂?

由于Handler發(fā)送消息時(shí),最終會(huì)調(diào)用sendMessageDelayed()胚鸯,這個(gè)方法會(huì)調(diào)用sendMessageAtTime()骨稿,在這個(gè)方法里會(huì)用系統(tǒng)時(shí)間加上傳入的delayed值,而系統(tǒng)時(shí)間是每時(shí)每刻都在變化的姜钳,所以幾乎不可能會(huì)有兩個(gè)時(shí)間相等的消息坦冠。

2、ThreadLocal是什么傲须?

ThreadLocal里存的是<key,value>的鍵值對(duì)蓝牲,有點(diǎn)像SparseArray,但是與之不同的是SparseArray內(nèi)部維護(hù)了2個(gè)數(shù)組泰讽,而ThreadLocal內(nèi)部只有一個(gè)數(shù)組,它存數(shù)據(jù)都是成對(duì)的往里存昔期,key存到index中已卸,value就存在了index+1的位置。其中key存的是ThreadLocal硼一,value存的是Looper累澡。

3、ThreadLocalMap是什么般贼?

它是ThreadLocal中的一個(gè)內(nèi)部類(lèi)愧哟,每個(gè)Thread中都會(huì)有一個(gè)全局的ThreadLocalMap對(duì)象奥吩。

4、Message使用了享元設(shè)計(jì)模式蕊梧。

5霞赫、App的啟動(dòng)流程?

在Launcher界面點(diǎn)擊應(yīng)用圖標(biāo) -》 啟動(dòng)Application -》zygote為應(yīng)用程序分配一個(gè)虛擬機(jī) -》 啟動(dòng)ActivityThread -》在ActivityThread中的main()函數(shù)對(duì)主線程的looper初始化并運(yùn)行肥矢。
*ActivityThread中存在一個(gè)main函數(shù)端衰,是應(yīng)用的入口,所以首先啟動(dòng)的是ActivityThread甘改。在

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旅东,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子十艾,更是在濱河造成了極大的恐慌抵代,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忘嫉,死亡現(xiàn)場(chǎng)離奇詭異主守,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)榄融,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)参淫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人愧杯,你說(shuō)我怎么就攤上這事涎才。” “怎么了力九?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵耍铜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我跌前,道長(zhǎng)棕兼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任抵乓,我火速辦了婚禮伴挚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灾炭。我一直安慰自己茎芋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布蜈出。 她就那樣靜靜地躺著田弥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铡原。 梳的紋絲不亂的頭發(fā)上偷厦,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天商叹,我揣著相機(jī)與錄音,去河邊找鬼只泼。 笑死剖笙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辜妓。 我是一名探鬼主播枯途,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼籍滴!你這毒婦竟也來(lái)了酪夷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤孽惰,失蹤者是張志新(化名)和其女友劉穎晚岭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體勋功,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坦报,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狂鞋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片片择。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖骚揍,靈堂內(nèi)的尸體忽然破棺而出字管,到底是詐尸還是另有隱情,我是刑警寧澤信不,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布嘲叔,位于F島的核電站,受9級(jí)特大地震影響抽活,放射性物質(zhì)發(fā)生泄漏硫戈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一下硕、第九天 我趴在偏房一處隱蔽的房頂上張望丁逝。 院中可真熱鬧,春花似錦卵牍、人聲如沸果港。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至谢谦,卻和暖如春释牺,著一層夾襖步出監(jiān)牢的瞬間萝衩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工没咙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猩谊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓祭刚,卻偏偏與公主長(zhǎng)得像牌捷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涡驮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348