背景
今天發(fā)現(xiàn)有個(gè)App存在嚴(yán)重的內(nèi)存泄漏問題,通過(guò)安裝LeakCanary找到大致方向,通過(guò)嚴(yán)格管理Timer、Presenter和Handler,輕松把它消耗的內(nèi)容降低了100M。
過(guò)程
LeakCanary
LeakCanary是square公司提供的一個(gè)很好用的內(nèi)存泄漏查找工具,與MAT不同的是,它會(huì)在你運(yùn)行App時(shí)檢查你的內(nèi)存回收,并找到內(nèi)存泄漏點(diǎn),直接提示給你,比用MAT去分析日志的形式更加簡(jiǎn)單直接,不過(guò)缺點(diǎn)是LeakCanary的信息不像MAT那么多,很難查到更細(xì)微的問題。
LeakCanary的安裝過(guò)程很簡(jiǎn)單,首先配置Gradle

然后在自定義的Application中初始化

記得在Manifest中使用自定義的Application

運(yùn)行App,LeakCanary會(huì)在App運(yùn)行過(guò)程中不定期收集信息,如果發(fā)現(xiàn)有內(nèi)存泄漏的問題,就會(huì)在通知欄中給出提示,如XXXActivity大概因?yàn)閄XX泄露了XXM內(nèi)存,比如我們運(yùn)行LeakCanary在Github上的demo,得到的提示就會(huì)像下圖這樣

可見,直接指出了問題類和問題原因,比起MAT還是簡(jiǎn)單多了的
Timer
在檢查中,在Presenter里忘記關(guān)閉一個(gè)Timer的task,在Activity的onDestroy事件中調(diào)用Presenter的onDestroy

在Presenter的onDestroy中執(zhí)行銷毀操作

我們看到,在Presenter銷毀元素時(shí),做了三件事:
1.關(guān)閉線程(要先執(zhí)行interrupt,否則休眠線程無(wú)法立即停止)
2.銷毀Presenter中關(guān)聯(lián)的View對(duì)象(否則會(huì)導(dǎo)致對(duì)應(yīng)的Activity無(wú)法釋放)
3.退出Timer定時(shí)任務(wù)
Presenter
再看一遍Presenter執(zhí)行銷毀操作的代碼

在上圖中,我們看到Presenter在銷毀時(shí),一定要把View銷毀掉,否則會(huì)導(dǎo)致對(duì)應(yīng)的Activity或Fragment無(wú)法釋放,在檢查中發(fā)現(xiàn)有些Presenter沒有寫銷毀,考慮在以后統(tǒng)一實(shí)現(xiàn)一個(gè)Presenter的基類,在基類中實(shí)現(xiàn)銷毀View的代碼
Handler
在檢查中,發(fā)現(xiàn)有些對(duì)話框彈出后無(wú)法回收內(nèi)存,在檢查中發(fā)現(xiàn)了這樣的代碼

這是我們一般接觸到的典型的Handler寫法,但是注意看提示,提示中說(shuō)明了這里有泄漏風(fēng)險(xiǎn),從避免內(nèi)存泄漏的角度,應(yīng)該改成這樣的代碼

在這段寫法中,我們用static內(nèi)部類去重載了一個(gè)Handler,static內(nèi)部類實(shí)際上會(huì)生成一個(gè)弱引用對(duì)象,這就不會(huì)產(chǎn)生內(nèi)存泄漏。
不過(guò)這樣一來(lái),在static內(nèi)部類中,我們就無(wú)法調(diào)用Activity的函數(shù)了,這就需要在這個(gè)Handler初始化時(shí)把Activity傳進(jìn)來(lái),直接傳進(jìn)來(lái)的Activity還是可能造成內(nèi)存泄漏,我們還要把它放到一個(gè)弱引用對(duì)象里,通過(guò)get()函數(shù)取得Activity對(duì)象并調(diào)用其函數(shù)。
總結(jié)
在這次的內(nèi)存泄漏查找過(guò)程中,主要解決了三個(gè)問題
1.及時(shí)銷毀所有的Timer定時(shí)任務(wù)
2.及時(shí)銷毀Presenter中的View對(duì)象
3.檢查所有的Handler,改為static+弱引用的實(shí)現(xiàn)方式
解決這些問題后,再次運(yùn)行LeakCanary,已經(jīng)不再輸出內(nèi)存泄漏的內(nèi)容了,App消耗的內(nèi)存也減少了100多M。
其實(shí)這里解決的內(nèi)存泄漏問題都是很淺顯的部分,能在這些地方出錯(cuò),說(shuō)明這個(gè)App的內(nèi)存泄漏已經(jīng)相當(dāng)嚴(yán)重了,在日常編碼中,還是要多留一份心才是。