Handler是我们经常使用的一个工具,如果细心的同学会发现,当我们在声明一个handler对象时,android lint往往会提示一个可能导致内存泄漏的警告,如下图:
那么Handler到底是如何导致内存泄漏的呢?
当Android程序第一次创建的时候,在主线程同时会创建一个Looper对象。Looper实现了一个简单的消息队列,一个接着一个处理Message对象。程序框架所有主要的事件(例如:屏幕上的点击时间,Activity生命周期的方法等等)都包含在Message对象中,然后添加到Looper的消息队列中,一个一个处理。主线程的Looper存在整个应用程序的生命周期内。
当一个Handler对象在主线程中创建的时候,它会关联到Looper的 message queue 。Message添加到消息队列中的时候Message会持有当前Handler引用,当Looper处理到当前消息的时候,会调用Handler#handleMessage(Message).
在java中,no-static的内部类会 隐式的 持有当前类的一个引用。static的类则没有。
当Activity结束后,在 Message queue 处理这个Message之前,它会持续存活着。这个Message持有Handler的引用,而Handler有持有Activity的引用,这个Activity所有的资源,在这个消息处理之前都不能也不会被回收,所以发生了内存泄露。
看一段导致内存泄漏的代码,可以结合LeakCanary工具跟踪内存泄漏信息
public class SampleActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 发送一个10分钟后执行的一个消息
mHandler.postDelayed(new Runnable() {
@Override
public void run() { }
}, 600000);
// 结束当前的Activity
finish();
}
}
知道原理后,那么该如何解决呢?
方法有两种,第一种是从代码逻辑上,我们在activity onDestroy方法中,对handler进行解绑;(不推荐,面向过程)
另一种,就是直接将handler进行静态改造,断开Handler 和外部class的联系。(推荐)
然而声明了handler为静态时,问题来了,静态类使用的方法也需要静态,难道我要在Activity中通过申明大量的static 变量提供给handler 使用???
这样的代码不仅难看而且设计很不合理,所以我们的方法如下因为你在MyHandler 有一个mActivity的弱引用,然后可以调用mActivity的任何public 函数和变量。所以,你只需要 在mAcivity 中定义 public XXX getXXX()方法把你需要提供给MyHandler 的变量封装起来,就可以啦。
static class MyHandler extends Handler {
WeakReference mActivity;
MyHandler(PopupActivity activity) {
mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
PopupActivity theActivity = mActivity.get();
switch (msg.what) {
case 0:
theActivity.popPlay.setChecked(true);
break;
}
}
};