前言
今天在看程序員每天面試一題夫椭,看到了這樣的一個(gè)面試題:關(guān)于 Handler 的內(nèi)存泄露劣欢,及怎么樣處理?
這是一個(gè)老生常談的問題湿弦,網(wǎng)上也是有很多文章講解瓤漏。而且大部分的講解都是大同小異,所以如果你已經(jīng)了解過了那基本可以不用看本篇了颊埃。
寫下來的主要目的是記錄一下自己的了解蔬充、所得。
Part1
為什么會(huì)泄露班利?
要講為什么會(huì)泄露饥漫,那就要從 Handler 的工作機(jī)制開始講起
1.Android 的主線程在一開始啟動(dòng)的時(shí)候就已經(jīng)默認(rèn)的常見了一個(gè) Looper 實(shí)例了,這個(gè)實(shí)例主要是用于處理一個(gè)一個(gè)的處理消息隊(duì)列中的 Message,它的生命周期是跟整個(gè)應(yīng)用的生命周期一樣長(zhǎng)的罗标。
2.當(dāng)我們使用 Handler 發(fā)送一個(gè) Message 到Looper 處理的消息隊(duì)列的時(shí)候趾浅,這個(gè) Message就已經(jīng)包含了改 handler 的實(shí)例了,這樣 Looper在處理到該 Message 的時(shí)候才可以調(diào)用 handler.handlerMessage()馒稍。
3.在Java 的非靜態(tài)內(nèi)部類或匿名內(nèi)部類會(huì)隱式持有外部類的實(shí)例皿哨。
so,在發(fā)送出去的 Message 還沒有被處理之前纽谒,該 Message 持有的 Handler 就不會(huì)被回收证膨,導(dǎo)致該Handler所在的外部類也不會(huì)回收。
Talk is cheep , show me the code! 無 code 言屌~
假設(shè)我們?cè)?Activity 發(fā)送了這樣的一個(gè) Message:
在 Activi 創(chuàng)建的時(shí)候發(fā)送一個(gè)定時(shí)十分鐘的消息鼓黔,此時(shí)在十分鐘還沒有到的時(shí)候央勒,我們按 Back 鍵,或者調(diào)用 finish結(jié)束當(dāng)前的 Activity澳化,該 Activity 其實(shí)并不會(huì)被回收崔步,因?yàn)樗膶?shí)例被 handler 隱式持有了,而 handler 又被 message 持有缎谷,此時(shí) message 又安安靜靜的躺在 Looper 的消息隊(duì)列中等待時(shí)間到被處理井濒。所以這樣子內(nèi)存泄露了。
Part2
how to fix列林?
既然知道是了非靜態(tài)內(nèi)部類或者匿名內(nèi)部類持有外部類而引起的瑞你,那么我們肯定就是從這方面入手了。
1.把 Handler 放在單獨(dú)的類文件希痴,或者使用靜態(tài)內(nèi)部類者甲,這樣避免持有外部類引用。
2.在靜態(tài)內(nèi)部類或是單獨(dú)的類文件中需要用到外部類的時(shí)候砌创,用弱引用來處理虏缸,在使用時(shí)再對(duì)該引用進(jìn)行 null 判斷
3.另外關(guān)于同樣也需要將Runnable設(shè)置為靜態(tài)的成員屬性
那么照著這樣的思路鲫懒,修改之后的代碼如下:
在經(jīng)過修改之后,handler 就不會(huì)持有 activity 的應(yīng)用刽辙,這樣子 Activity 該被干掉還是會(huì)被干掉窥岩,改回收的東西照樣會(huì)回收。
by the way:
在 handler中一定要重寫 handleMessage()然后一定要進(jìn)行非空判斷扫倡。
不然定時(shí)到了,Looper 調(diào)用了 message 的 handler.handleMessage(),然后就會(huì) nullPointException 竟纳,APP 就會(huì) crash了~