在上一篇文章《Android Handler機制完全解析》中,我們從源碼的角度分析了Hanlder機制,接下來繼續(xù)學(xué)習(xí)Handler,本篇文章主要講解的是Handler可能會導(dǎo)致的內(nèi)存泄漏以及解決方案。
1.為什么會發(fā)生內(nèi)存泄漏
在平時使用Handler的時候,我們通常會這樣定義:
// 定義一個Handler對象,并實現(xiàn)handleMessage方法
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 在此接收子線程發(fā)送的消息
}
};
嗯,看起來沒有什么問題,但是Android Lint卻給出了警告:
This Handler class should be static or leaks might occur
意思是說這個Handler應(yīng)該定義成靜態(tài)的,否則可能會內(nèi)存溢出。什么鬼,怎么還會內(nèi)存溢出,于是百...額...不對...Google一下,發(fā)現(xiàn)Google的工程師Romain Guy已經(jīng)在論壇中做出過解釋:
I wrote that debugging code because of a couple of memory leaks I found in the Android codebase. Like you said, a Message has a reference to the Handler which, when it's inner and non-static, has a reference to the outer this (an Activity for instance.) If the Message lives in the queue for a long time, which happens fairly easily when posting a delayed message for instance, you keep a reference to the Activity and "leak" all the views and resources. It gets even worse when you obtain a Message and don't post it right away but keep it somewhere (for instance in a static structure) for later use.
學(xué)好一門外語是多么的重要,翻譯翻譯:
我在Android代碼庫中發(fā)現(xiàn)了一些內(nèi)存泄漏,為此我寫了調(diào)試代碼進(jìn)行測試,像你說的一樣,Message會持有一個對Handler的引用,當(dāng)這個Handler是非靜態(tài)內(nèi)部類的時候,又會持有一個對外部類的引用(比如Activity)。如果發(fā)送一條延時的Message,由于這個Message會長期存在于隊列中,就會導(dǎo)致Handler長期持有對Activity的引用,從而引起視圖和資源泄漏。當(dāng)你發(fā)送一條延時的Mesaage,并且把這個Message保存在某些地方(例如靜態(tài)結(jié)構(gòu)中)備用,內(nèi)存泄漏會變得更加嚴(yán)重。
先說下什么是內(nèi)存泄漏:
內(nèi)存泄漏(Memory Leak)是指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
寫段代碼說明一下:
public class HandlerActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
handler.sendEmptyMessageDelayed(0, 10 * 60 * 1000);
finish();
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// ...
}
};
}
使用Handler發(fā)送一條延時消息,然后關(guān)閉Activity,在Activity被關(guān)閉之后,Message將在消息隊列中存在10分鐘才能被執(zhí)行到,這個Message持有一個對Handler的引用,Handler又持有一個對當(dāng)前Activity的引用,這些引用會在Message被執(zhí)行之前一直保持,這樣當(dāng)前Activity就不會被垃圾回收機制回收,從而導(dǎo)致內(nèi)存泄漏。
2.如何解決
那么,該如何解決這個問題呢,Romain Guy給出了他的建議寫法:
class OuterClass {
class InnerClass {
private final WeakReference<OuterClass> mTarget;
InnerClass(OuterClass target) {
mTarget = new WeakReference<OuterClass>(target);
}
void doSomething() {
OuterClass target = mTarget.get();
if (target != null) {
target.do();
}
}
}
在內(nèi)部類的構(gòu)造方法中,創(chuàng)建一個對外部類的弱引用,然后再內(nèi)部類的方法中通過弱引用獲取外部類對象,進(jìn)行非空判斷后再進(jìn)行操作,OK,修改一下我們的代碼:
public class HandlerActivity extends BaseActivity {
@Bind(R.id.tv_handler)
TextView tvHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
ButterKnife.bind(this);
new WeakHandler(this).sendEmptyMessageDelayed(0, 10 * 60 * 1000);
finish();
}
private static class WeakHandler extends Handler {
WeakReference<HandlerActivity> weakReference;
public WeakHandler(HandlerActivity activity) {
weakReference = new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = weakReference.get();
if (activity != null && activity.tvHandler != null) {
activity.tvHandler.setText("收到Handler發(fā)送的消息");
}
}
}
}
因為靜態(tài)內(nèi)部類不會持有對外部類的引用,所以定義一個靜態(tài)的Handler,這樣Acitivity就不會被泄漏了,同時讓Handler持有一個對Activity的弱引用,這樣就可以happy的在Handler中調(diào)用Activity中的資源或者方法了。
如果在關(guān)閉Activity之后,不需要對消息隊列中的消息進(jìn)行處理了,可以在onDestory方法中加入下面這段代碼:
// 移除所有消息
handler.removeCallbacksAndMessages(null);
// 移除單條消息
handler.removeMessages(what);
嗯,Handler可能導(dǎo)致的內(nèi)存泄漏以及解決方案到這里就講完了。
3.寫在最后
歡迎同學(xué)們吐槽評論,如果你覺得本篇博客對你有用,那么就留個言或者點下喜歡吧(^-^)