Android中使用Handler造成內(nèi)存泄露的分析和解決辦法
問題描述: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ì)列中長達(dá)10分鐘的時間才會被執(zhí)行到。這個Message持有一個對Handler的引用,Handler也會持有一個對于外部類(SampleActivity)的隱式引用,這些引用在Message被執(zhí)行前將一直保持,這樣會保證Activity的上下文不被垃圾回收機(jī)制回收,同時也會泄露應(yīng)用程序的資源(views and resources)。 ADT20以后加入了一條新的檢查規(guī)則:確保類內(nèi)部的handler不含有對外部類的隱式引用 。
常見寫法
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;
}
}
};
上述寫法引起泄露原因是:
當(dāng)Android應(yīng)用啟動的時候,會先創(chuàng)建一個應(yīng)用主線程的Looper對象,Looper實(shí)現(xiàn)了一個簡單的消息隊(duì)列,一個一個的處理里面的Message對象。主線程Looper對象在整個應(yīng)用生命周期中存在。
當(dāng)在主線程中初始化Handler時,該Handler和Looper的消息隊(duì)列關(guān)聯(lián)。發(fā)送到消息隊(duì)列的Message會引用發(fā)送該消息的Handler對象,這樣系統(tǒng)可以調(diào)用 Handler#handleMessage(Message) 來分發(fā)處理該消息。
在Java中,非靜態(tài)(匿名)內(nèi)部類會引用外部類對象。而靜態(tài)內(nèi)部類不會引用外部類對象。
-如果外部類是Activity,則會引起Activity泄露 。
- 當(dāng)Activity finish后,延時消息會繼續(xù)存在主線程消息隊(duì)列中1分鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導(dǎo)致該Activity對象無法被回收,從而導(dǎo)致了上面說的 Activity泄露。
為解決這個問題,下面這段代碼中的Handler則是一個靜態(tài)匿名內(nèi)部類。靜態(tài)匿名內(nèi)部類不會持有一個對外部類的隱式引用,因此Activity將不會被泄露。如果你需要在Handler中調(diào)用外部Activity的方法,就讓Handler持有一個對Activity的WeakReference,這樣就不會泄露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;
}
}
}
}
同時我們盡量要在當(dāng)前Activity的生命周期內(nèi)結(jié)束對所有回調(diào)函數(shù)和message的引用
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
至此一個可以內(nèi)存泄露的問題輕松處理,get!