在上一篇文章《Android Handler機(jī)制完全解析》中,我們從源碼的角度分析了Hanlder機(jī)制出牧,接下來(lái)繼續(xù)學(xué)習(xí)Handler,本篇文章主要講解的是Handler可能會(huì)導(dǎo)致的內(nèi)存泄漏以及解決方案。
1.為什么會(huì)發(fā)生內(nèi)存泄漏
在平時(shí)使用Handler的時(shí)候蒂萎,我們通常會(huì)這樣定義:
// 定義一個(gè)Handler對(duì)象猎物,并實(shí)現(xiàn)handleMessage方法
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 在此接收子線程發(fā)送的消息
}
};
嗯虎囚,看起來(lái)沒(méi)有什么問(wèn)題,但是Android Lint卻給出了警告:
This Handler class should be static or leaks might occur
意思是說(shuō)這個(gè)Handler應(yīng)該定義成靜態(tài)的蔫磨,否則可能會(huì)內(nèi)存溢出淘讥。什么鬼,怎么還會(huì)內(nèi)存溢出堤如,于是百...額...不對(duì)...Google一下蒲列,發(fā)現(xiàn)Google的工程師Romain Guy已經(jīng)在論壇中做出過(guò)解釋:
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é)好一門外語(yǔ)是多么的重要,翻譯翻譯:
我在Android代碼庫(kù)中發(fā)現(xiàn)了一些內(nèi)存泄漏搀罢,為此我寫了調(diào)試代碼進(jìn)行測(cè)試蝗岖,像你說(shuō)的一樣,Message會(huì)持有一個(gè)對(duì)Handler的引用榔至,當(dāng)這個(gè)Handler是非靜態(tài)內(nèi)部類的時(shí)候抵赢,又會(huì)持有一個(gè)對(duì)外部類的引用(比如Activity)。如果發(fā)送一條延時(shí)的Message唧取,由于這個(gè)Message會(huì)長(zhǎng)期存在于隊(duì)列中铅鲤,就會(huì)導(dǎo)致Handler長(zhǎng)期持有對(duì)Activity的引用,從而引起視圖和資源泄漏枫弟。當(dāng)你發(fā)送一條延時(shí)的Mesaage邢享,并且把這個(gè)Message保存在某些地方(例如靜態(tài)結(jié)構(gòu)中)備用,內(nèi)存泄漏會(huì)變得更加嚴(yán)重淡诗。
先說(shuō)下什么是內(nèi)存泄漏:
內(nèi)存泄漏(Memory Leak)是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放驼仪,造成系統(tǒng)內(nèi)存的浪費(fèi)掸犬,導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
寫段代碼說(shuō)明一下:
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ā)送一條延時(shí)消息绪爸,然后關(guān)閉Activity湾碎,在Activity被關(guān)閉之后,Message將在消息隊(duì)列中存在10分鐘才能被執(zhí)行到奠货,這個(gè)Message持有一個(gè)對(duì)Handler的引用介褥,Handler又持有一個(gè)對(duì)當(dāng)前Activity的引用,這些引用會(huì)在Message被執(zhí)行之前一直保持递惋,這樣當(dāng)前Activity就不會(huì)被垃圾回收機(jī)制回收柔滔,從而導(dǎo)致內(nèi)存泄漏。
2.如何解決
那么萍虽,該如何解決這個(gè)問(wèn)題呢睛廊,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)建一個(gè)對(duì)外部類的弱引用杉编,然后再內(nèi)部類的方法中通過(guò)弱引用獲取外部類對(duì)象超全,進(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ā)送的消息");
}
}
}
}
因?yàn)殪o態(tài)內(nèi)部類不會(huì)持有對(duì)外部類的引用嘶朱,所以定義一個(gè)靜態(tài)的Handler,這樣Acitivity就不會(huì)被泄漏了光酣,同時(shí)讓Handler持有一個(gè)對(duì)Activity的弱引用疏遏,這樣就可以happy的在Handler中調(diào)用Activity中的資源或者方法了。
如果在關(guān)閉Activity之后救军,不需要對(duì)消息隊(duì)列中的消息進(jìn)行處理了财异,可以在onDestory方法中加入下面這段代碼:
// 移除所有消息
handler.removeCallbacksAndMessages(null);
// 移除單條消息
handler.removeMessages(what);
嗯,Handler可能導(dǎo)致的內(nèi)存泄漏以及解決方案到這里就講完了唱遭。
3.寫在最后
歡迎同學(xué)們吐槽評(píng)論戳寸,如果你覺(jué)得本篇博客對(duì)你有用,那么就留個(gè)言或者點(diǎn)下喜歡吧(^-^)