1显晶、自定義 Handler 時(shí)如何有效地避免內(nèi)存泄漏問(wèn)題

分析:

在Android系統(tǒng)中,Handler是一個(gè)消息發(fā)送和處理機(jī)制的核心組件之一壹士,與之配套的其他主要組件還有Looper和Message磷雇,MessageQueue。
Message和Runnable類(lèi)是消息的載體躏救。MessageQueue是消息等待的隊(duì)列倦春。Looper則負(fù)責(zé)從隊(duì)列中取消息。

Handler有兩個(gè)主要作用:

1.安排調(diào)度(scheule)消息和可執(zhí)行的runnable落剪,可以立即執(zhí)行睁本,也可以安排在某個(gè)將來(lái)的時(shí)間點(diǎn)執(zhí)行。

2.讓某一個(gè)行為(action)在其他線程中執(zhí)行忠怖。

Handler是由系統(tǒng)所提供的一種異步消息處理的常用方式,一般情況下不會(huì)發(fā)生內(nèi)存泄露呢堰。
Handler為什么可能造成內(nèi)存泄漏。這里的內(nèi)存泄漏凡泣,常常指的是泄漏了Activity等組件枉疼。

public class ShanActivity extends Activity{
    public Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
             
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    }
}

這有什么問(wèn)題呢。問(wèn)題在于該Handler的實(shí)例采用了內(nèi)部類(lèi)的寫(xiě)法鞋拟,它是ShanActivity這個(gè)實(shí)例的內(nèi)部類(lèi)骂维,在Java中,關(guān)于內(nèi)部類(lèi)有一個(gè)特點(diǎn):在java中贺纲,非靜態(tài)的內(nèi)部類(lèi)和匿名內(nèi)部類(lèi)都會(huì)隱式的持有一個(gè)外部類(lèi)的引用航闺。所以,該handler實(shí)例持有了ShanActivity的一個(gè)引用猴誊。

生命周期較長(zhǎng)的組件引用了生命周期較短的組件潦刃。Handler就是一種典型的示例,以上面的代碼舉例懈叹。ShanActivity可能會(huì)被泄漏乖杠,也就是該組件沒(méi)有用了,比如調(diào)用了finish()后澄成,垃圾回收器卻遲遲沒(méi)有回收該Activity胧洒。原因出在該實(shí)例的handler內(nèi)部類(lèi)引用了它,而該handler實(shí)例可能被MessageQueue引用著墨状。

問(wèn)題原因:

一般非靜態(tài)內(nèi)部類(lèi)持有外部類(lèi)的引用的情況下卫漫,造成外部類(lèi)在使用完成后不能被系統(tǒng)回收內(nèi)存,從而造成內(nèi)存泄漏歉胶。這里 Handler 持有外部類(lèi) Activity 的引用汛兜,而handler有又未處理完的message,一旦 Activity 被銷(xiāo)毀通今,而此時(shí) Handler 依然持有 Activity 引用粥谬,就會(huì)造成內(nèi)存泄漏肛根。

解決方法:

1.保證Activity被finish()時(shí)該線程的消息隊(duì)列沒(méi)有這個(gè)Activity的handler內(nèi)部類(lèi)的引用。這個(gè)場(chǎng)景是及其常見(jiàn)的漏策,因?yàn)閔andler經(jīng)常被用來(lái)發(fā)延時(shí)消息派哲。一個(gè)補(bǔ)救的辦法就是在該類(lèi)需要回收的時(shí)候,手動(dòng)地把消息隊(duì)列中的消息清空:mHandler.removeCallbacksAndMessages(null);

2.要么讓這個(gè)handler不持有Activity等外部組件實(shí)例掺喻,讓該Handler成為靜態(tài)內(nèi)部類(lèi)芭届。(靜態(tài)內(nèi)部類(lèi)是不持有外部類(lèi)的實(shí)例的,因而也就調(diào)用不了外部的實(shí)例方法了)

3.在2方法的基礎(chǔ)上感耙,為了能調(diào)用外部的實(shí)例方法褂乍,傳遞一個(gè)外部的弱引用進(jìn)來(lái))

4.將Handler放到抽取出來(lái)放入一個(gè)單獨(dú)的頂層類(lèi)文件中。

這里需要了解一下關(guān)于Java里面引用的知識(shí):

強(qiáng)引用(Strong Reference) 默認(rèn)引用即硼。如果一個(gè)對(duì)象具有強(qiáng)引用逃片,垃圾回收器絕不會(huì)回收它。在內(nèi)存空 間不足時(shí)只酥,Java虛擬機(jī)寧愿拋出OutOfMemory的錯(cuò)誤褥实,使程序異常終止,也不會(huì)強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足問(wèn)題裂允。
軟引用(SoftReference) 如果內(nèi)存空間足夠损离,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了绝编,就會(huì)回收這些對(duì)象的內(nèi)存僻澎。
弱引用(WeakReference 在垃圾回收器一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否瓮增,都會(huì)回收它的內(nèi)存怎棱。
虛引用(PhantomReference) 如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣绷跑,在任何時(shí)候都可能被垃圾回收。

第三種凡资,需要一些額外的代碼砸捏,比較通用。

public class ShanActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<ShanActivity> mActivity;
public MyHandler(ShanActivity activity) {
  mActivity = new WeakReference<ShanActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
  ShanActivity activity = mActivity.get();
  if (activity != null) {
     //do Something
  }
}
}

第四種方式隙赁,抽取做單獨(dú)封裝垦藏。

/**
 * 實(shí)現(xiàn)回調(diào)弱引用的Handler
 * 防止由于內(nèi)部持有導(dǎo)致的內(nèi)存泄露
 * 傳入的Callback不能使用匿名實(shí)現(xiàn)的變量,必須與使用這個(gè)Handle的對(duì)象的生命周期一 
 * 致否則會(huì)被立即釋放掉了
 */
public class WeakRefHandler extends Handler {
    private WeakReference<Callback> mWeakReference;
    
    public WeakRefHandler(Callback callback) {
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    public WeakRefHandler(Callback callback, Looper looper) {
        super(looper);
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    @Override
    public void handleMessage(Message msg) {
        if (mWeakReference != null && mWeakReference.get() != null) {
            Callback callback = mWeakReference.get();
            callback.handleMessage(msg);
        }
    }
}

由于是弱引用伞访,當(dāng)該類(lèi)需要被回收時(shí)掂骏,可以直接被回收掉。

WeakRefHandler的使用時(shí)如下:

    private Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch(msg.what){
            }
            return true;
        }
    };
    private Handler mHandler = new WeakRefHandler(mCallback);

總結(jié):

handler改為弱引用不是一概而論(大家只考慮在activity問(wèn)題厚掷,handler持有activity的引用)弟灼,解決問(wèn)題可以傳activity弱引用給handler就行级解,如果在service后臺(tái)用到handler,難道也弱引用田绑?合理使用handler勤哗,要明白為什么泄漏,不是所有場(chǎng)景都能用弱引用

tip:

removeCallbacksAndMessages: remove所有message和runnable
removeCallbacks: 只remove message掩驱,無(wú)法remove runnable

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芒划,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子欧穴,更是在濱河造成了極大的恐慌民逼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涮帘,死亡現(xiàn)場(chǎng)離奇詭異缴挖,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)焚辅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)映屋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人同蜻,你說(shuō)我怎么就攤上這事棚点。” “怎么了湾蔓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵瘫析,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我默责,道長(zhǎng)贬循,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任桃序,我火速辦了婚禮杖虾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媒熊。我一直安慰自己奇适,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布芦鳍。 她就那樣靜靜地躺著嚷往,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柠衅。 梳的紋絲不亂的頭發(fā)上皮仁,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼贷祈。 笑死趋急,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的付燥。 我是一名探鬼主播宣谈,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼键科!你這毒婦竟也來(lái)了闻丑?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勋颖,失蹤者是張志新(化名)和其女友劉穎嗦嗡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體饭玲,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侥祭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茄厘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矮冬。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖次哈,靈堂內(nèi)的尸體忽然破棺而出胎署,到底是詐尸還是另有隱情,我是刑警寧澤窑滞,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布琼牧,位于F島的核電站,受9級(jí)特大地震影響哀卫,放射性物質(zhì)發(fā)生泄漏巨坊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一此改、第九天 我趴在偏房一處隱蔽的房頂上張望趾撵。 院中可真熱鬧,春花似錦带斑、人聲如沸鼓寺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至敢靡,卻和暖如春挂滓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啸胧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工赶站, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幔虏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓贝椿,卻偏偏與公主長(zhǎng)得像想括,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烙博,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349