Android 常见的Handler内存泄露
1.问题发现:
在平时使用Handler的时候,我们通常会这样定义:
// 定义一个Handler对象,并实现handleMessage方法
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 在此接收子线程发送的消息
}
};
嗯,看起来没有什么问题,但是Android Lint却给出了警告:
This Handler class should be static or leaks might occur
意思是说这个Handler应该定义成静态的,否则可能会内存溢出。什么鬼,怎么还会内存溢出,于是百...额...不对...Google一下,发现Google的工程师Romain Guy已经在论坛中做出过解释:
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.
学好一门外语是多么的重要,翻译翻译:
我在Android代码库中发现了一些内存泄漏,为此我写了调试代码进行测试,像你说的一样,Message会持有一个对Handler的引用,当这个Handler是非静态内部类的时候,又会持有一个对外部类的引用(比如Activity)。如果发送一条延时的Message,由于这个Message会长期存在于队列中,就会导致Handler长期持有对Activity的引用,从而引起视图和资源泄漏。当你发送一条延时的Mesaage,并且把这个Message保存在某些地方(例如静态结构中)备用,内存泄漏会变得更加严重。
先说下什么是内存泄漏:
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
写段代码说明一下:
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);
// ...
}
};
}
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();
}
}
}
在内部类的构造方法中,创建一个对外部类的弱引用,然后再内部类的方法中通过弱引用获取外部类对象,进行非空判断后再进行操作,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发送的消息");
}
}
}
}
因为静态内部类不会持有对外部类的引用,所以定义一个静态的Handler,这样Acitivity就不会被泄漏了,同时让Handler持有一个对Activity的弱引用,这样就可以happy的在Handler中调用Activity中的资源或者方法了。
推荐阅读
-
Javascript 闭包引起的IE内存泄露分析_javascript技巧
-
Android Handler的使用详解
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
Android实现内存中数据保存到sdcard的方法
-
详解使用Xcode7的Instruments检测解决iOS内存泄露(最新)
-
Android中ListView的几种常见的优化方法总结
-
Android获取App内存使用情况的方法
-
Android中LeakCanary检测内存泄漏的方法
-
Android实现常见的验证码输入框实例代码
-
Android编程开发之seekBar采用handler消息处理操作的方法