先看下面这段代码
public class LeakActivity extends Activity {
public Handler leakHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
}
};
这里简单的声明了一个handler,看起来一切正常,但是这种写法有可能会引起很大的内存泄露,原因其实编译器已经告诉你了,因为这种写法往往有一个警告:
在Android中 Handler应该被声明为 static 否则可能会引起内存泄露的问题
为什么可能会内存泄露呢? 我们来分析一些已有信息
当一个Android应用第一次启动的时候,系统会创建一个Loop对象在应用的主线程中,Loop就是一个消息队列(Message queue),处理一个又一个的信息。应用的主要事件比如:Activity的生命周期,点击事件等等,都包含在消息(message)对象中。消息会在Loop 的消息队列中排队等着被处理,所以这个主线程的Loop会在应用的整个生命周期中始终存在
现在我们的handler已经在主线程声明,很自然的他与主线程Loop的消息队列关联了起来。这时传到消息队列中的消息会带有handler的引用,因为当消息被处理时系统需要自己去调用Handler的
handleMessage(Message msg)
方法在java中 非静态的内部匿名类(也就是我们声明Handle的方式),会持有他的外部类的一个隐式的应用,当然静态的内部类是不会的。
基于以上几点修改一下这个例子
public class LeakActivity extends Activity {
public Handler leakHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
};
protected void onCreate(android.os.Bundle savedInstanceState) {
setContentView(R.layout.activity_leak);
//10分钟之后发送消息
leakHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}, 1000 * 60 * 10);
//退出到上一个Activity
finish();
};
声明的handler延时10分钟发送信息,同时finish()掉当前的Activity,根据上面说明的第二点,延时10分钟发送的信息,依然会在主线程的消息队列中并且等待10分钟后发送信息。那么问题来了由于消息持有handler的引用,而handler又是一个匿名内部类,匿名内部类会持有他的外部类(也就是LeakActivity)的一个隐式引用,而LeakActivity已经被finish()掉了,所以 就有可能会导致Context的内存泄露,注意一下上面的runnable也是一个匿名内部类
如何解决这个隐患呢,方法相信大家已经知道了,使用静态内部类。这样就不会去持有外部的隐式引用。
public class LeakActivity extends Activity {
/**
* 声明静态内部类不会持有外部类的隐式引用
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* 这里的Runnable也是
* 声明静态内部类不会持有外部类的隐式引用
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//10分钟之后发送消息
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// 退回到上一个Activity
finish();
}
}
将handler和runnable都声明为静态内部类,同时让handler持有的Context声明为WeakReference 。
FBI Warning
如果内部类可以存在于activity的生命周期之外,那么不要去使用非静态的内部类,因为他会持有外部类(activity)的隐式引用。把它声明成静态的