Android中使用Handler造成內(nèi)存泄露的分析和解決辦法
問(wèn)題描述:This Handler class should be static or leaks might occur (anonymous android.os.Handler)(參考 https://my.oschina.net/liucundong/blog/294127)
特性:當(dāng)Activity被finish()掉雄人,Message 將存在于消息隊(duì)列中長(zhǎng)達(dá)10分鐘的時(shí)間才會(huì)被執(zhí)行到医瘫。這個(gè)Message持有一個(gè)對(duì)Handler的引用欺冀,Handler也會(huì)持有一個(gè)對(duì)于外部類(SampleActivity)的隱式引用酱讶,這些引用在Message被執(zhí)行前將一直保持瑟枫,這樣會(huì)保證Activity的上下文不被垃圾回收機(jī)制回收责球,同時(shí)也會(huì)泄露應(yīng)用程序的資源(views and resources)渡紫。 ADT20以后加入了一條新的檢查規(guī)則:確保類內(nèi)部的handler不含有對(duì)外部類的隱式引用 乔外。
常見(jiàn)寫(xiě)法
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case 0:
break;
default:
break;
}
}
};
上述寫(xiě)法引起泄露原因是:
當(dāng)Android應(yīng)用啟動(dòng)的時(shí)候,會(huì)先創(chuàng)建一個(gè)應(yīng)用主線程的Looper對(duì)象纠炮,Looper實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的消息隊(duì)列月趟,一個(gè)一個(gè)的處理里面的Message對(duì)象。主線程Looper對(duì)象在整個(gè)應(yīng)用生命周期中存在恢口。
當(dāng)在主線程中初始化Handler時(shí)孝宗,該Handler和Looper的消息隊(duì)列關(guān)聯(lián)。發(fā)送到消息隊(duì)列的Message會(huì)引用發(fā)送該消息的Handler對(duì)象耕肩,這樣系統(tǒng)可以調(diào)用 Handler#handleMessage(Message) 來(lái)分發(fā)處理該消息因妇。
在Java中,非靜態(tài)(匿名)內(nèi)部類會(huì)引用外部類對(duì)象看疗。而靜態(tài)內(nèi)部類不會(huì)引用外部類對(duì)象沙峻。
-如果外部類是Activity,則會(huì)引起Activity泄露 两芳。
- 當(dāng)Activity finish后摔寨,延時(shí)消息會(huì)繼續(xù)存在主線程消息隊(duì)列中1分鐘,然后處理消息怖辆。而該消息引用了Activity的Handler對(duì)象是复,然后這個(gè)Handler又引用了這個(gè)Activity删顶。這些引用對(duì)象會(huì)保持到該消息被處理完,這樣就導(dǎo)致該Activity對(duì)象無(wú)法被回收淑廊,從而導(dǎo)致了上面說(shuō)的 Activity泄露逗余。
為解決這個(gè)問(wèn)題,下面這段代碼中的Handler則是一個(gè)靜態(tài)匿名內(nèi)部類季惩。靜態(tài)匿名內(nèi)部類不會(huì)持有一個(gè)對(duì)外部類的隱式引用录粱,因此Activity將不會(huì)被泄露赶促。如果你需要在Handler中調(diào)用外部Activity的方法间唉,就讓Handler持有一個(gè)對(duì)Activity的WeakReference,這樣就不會(huì)泄露Activity的上下文了
- 如下所示
private final MyHandler mHandler = new MyHandler(this);
static class MyHandler extends Handler {
private final WeakReference<CashActivity> mActivity;
public MyHandler(CashActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
CashActivity activity = mActivity.get();
switch (msg.what) {
case 0:
activity.submit_but.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
}
}
同時(shí)我們盡量要在當(dāng)前Activity的生命周期內(nèi)結(jié)束對(duì)所有回調(diào)函數(shù)和message的引用
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
至此一個(gè)可以內(nèi)存泄露的問(wèn)題輕松處理廉赔,get青抛!