首先我們看最常見(jiàn)的handler寫(xiě)法:
為什么handler會(huì)導(dǎo)致內(nèi)存泄露呢鞭铆?是因?yàn)閮?nèi)部類持有了外部類的引用引起的嗎墩剖?但是我們?nèi)粘?xiě)代碼中也有很多場(chǎng)景使用了內(nèi)部類薄湿,為啥那些情況沒(méi)有內(nèi)存泄露呢惊奇,今天我們就來(lái)分析根本本質(zhì)原因柒爵。
內(nèi)存泄露本質(zhì)就是已分配的內(nèi)存無(wú)法被回收
原因:長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用闸氮。
一般從引用鏈來(lái)分析內(nèi)存泄露原因:
首先我們上述寫(xiě)的就是Handler這個(gè)內(nèi)部類持有了外部了外部Activity引用鹦蠕,那么就是Handler->Activity士败,下面我們繼續(xù)看是誰(shuí)持有了Handler。
當(dāng)我們調(diào)用handler.sendMessage方法時(shí)最終會(huì)調(diào)用enqueueMessage方法
然后會(huì)把當(dāng)前handler 也就是this賦值給message的target屬性
那么到這里也就意味著message持有了Handler俘枫。所以我們Message->Handler->Activity 繼續(xù)看 直到我們找到可以作為Gcroot的腥沽。那么Message又被誰(shuí)持有呢?
在MessageQueue種有enqueueMessage添加消息往隊(duì)列中鸠蚪,這樣Message被MessageQueue所持有了今阳。MessageQueue->Message->Handler->Activity,繼續(xù)往下看
當(dāng)Looper的構(gòu)造方法中茅信,需要初始化messagequeue盾舌,此時(shí)消息隊(duì)列被Looper所持有。
Looper->MessageQueue->Message->Handler->Activity
可以看到主線程中ActivityThread的main方法中會(huì)調(diào)用Looper.prepareMainLooper方法蘸鲸,進(jìn)而構(gòu)造了一個(gè)Looper妖谴,然后把Looper放到TheadLocal,并放到主線程的ThreadLocalMap中酌摇,也就是說(shuō)這個(gè)looper是主線程持有的膝舅,是無(wú)法被釋放掉的。
ActivityThread->Looper->MessageQueue->Message->Handler->Activity窑多。
所以從這條引用鏈來(lái)看仍稀,當(dāng)Acticity被銷毀了,但是依然被ActvityThread所間接持有埂息,不會(huì)被回收技潘,造成內(nèi)存泄露。
解決方案:
1 使用靜態(tài)內(nèi)部類千康,根據(jù)java特性享幽,不會(huì)持有外部類的引用。Handler->Activity后面這條鏈就直接斷掉了吧秕。也就是Activity銷毀可以被回收了
2 弱引用加上靜態(tài)內(nèi)部類
弱引用引用的對(duì)象具有更短的生命周期琉闪,開(kāi)發(fā)者無(wú)需擔(dān)心內(nèi)存被占用沒(méi)有及時(shí)釋放導(dǎo)致內(nèi)存泄露問(wèn)題,但也會(huì)引發(fā)弱引用對(duì)象空指針問(wèn)題砸彬,需要注意判空颠毙。(kotlin有安全機(jī)制)
3 移除消息
會(huì)調(diào)用下面Message的recycleUnchecked方法
這里會(huì)將handler賦值為null,會(huì)清空MessageQueue里面所有的Message砂碉。也就是說(shuō)能把Message -> Handler的鏈條斷開(kāi)蛀蜜。達(dá)到ActivityThread不能間接持有Activity。
總結(jié):主要分析了Handler為何會(huì)導(dǎo)致內(nèi)存泄露增蹭,其實(shí)不是內(nèi)部類導(dǎo)致的滴某,而是Handler被長(zhǎng)時(shí)間生命周期對(duì)象所持有,形成了ActivityThread->Looper->MessageQueue->Message->Handler->Activity的持有鏈滋迈。才導(dǎo)致了Activity無(wú)法被回收霎奢。
解決方案:
1 使用Activity退出時(shí),清空MessageQueue的所有Message饼灿。將Handler置為Null幕侠。
2 使用static修飾內(nèi)部類,內(nèi)部類寫(xiě)出靜態(tài)的碍彭。
3 在Handler中晤硕,使用弱引用持有Activity對(duì)象,讓Activity對(duì)象生命周期更短庇忌,就能保證在垃圾回收時(shí)被正澄韫浚回收。