引例
先看一段代码:
public class SampleActivity extends Activity {
private final Handler mLeakHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
这段代码很可能导致内存泄露,Android Lint 会给出下列的警告:
This Handler class should be static or leaks might occur (anonymous android.os.Handler)
但是,哪里会导致内存泄露呢?内存泄漏又是怎么发生的呢?
分析
首先我们先说几点常识:
1. Android的Looper和Handler的一个过程:当Android application 启动之后,framework会创建一个Looper对象,接着Looper实现一个简单的消息队列,在一个消息循环中处理Message对象。主线程Looper存在于整个App的生命周期。
2. 在主线程中实例化Handler之后,它关联了Looper的消息队列,消息队列里面的Message对象会持有Handler的引用。这样是为了Hander能调用handler的handleMessage()方法去处理这个Message对象
3. 非静态的内部类和匿名类会隐式地持有一个他们外部类的引用。静态内部类则不会。
到底哪里发生了内存泄露?我们来看下列代码:
public class SampleActivity extends Activity {
private final Handler mLeakHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLeakHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000 * 60 * 10);
finish();
}
}
注意:代码中的
new Handler() {
4 @Override
5 public void handleMessage(Message msg) {
6 // ...
7 }
8 }
和
new Runnable() {
16 @Override
17 public void run() { }
18 }
都是匿名内部类。
这个Acitivity中,存在匿名内部类,当Activity被销毁之后,延时发送的消息会继续在主线程的消息队列中存活10分钟,直到他们被处理。这个消息持有这个Activity的Handler引用,这个Handler有隐式地持有他的外部类(在这个例子中是SampleActivity)。直到消息被处理前,这个引用都不会被释放。因此Activity不会被垃圾回收机制回收,泄露他所持有的应用程序资源。注意,匿名Runnable类也一样。匿名类的非静态实例持有一个隐式的外部类引用,因此context将被泄露。
静态内部类解决内存泄露
为了解决这个问题,Handler的子类应该定义在一个新文件中或使用静态内部类。静态内部类不会隐式持有外部类的引用。所以不会导致它的Activity泄露。如果你需要在Handle内部调用外部Activity的方法,那么让Handler持有一个Activity的弱引用(WeakReference)以便你不会意外导致context泄露。为了解决我们实例化匿名Runnable类可能导致的内存泄露,我们将用一个静态变量来引用他(因为匿名类的静态实例不会隐式持有他们外部类的引用)。
public class SampleActivity extends Activity {
//匿名类的静态实例不会隐式持有他们外部类的引用
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
}
};
private final MyHandler mHandler = new MyHandler(this);
/**
* 静态内部类的实例不会隐式持有他们外部类的引用。
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity mActivity) {
this.mActivity = new WeakReference<SampleActivity>(mActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
SampleActivity activity = mActivity.get();
if (activity != null) {
}
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000 * 60 * 10);
finish();
}
不在一个Activity中使用非静态内部类, 以防它的生命周期比Activity长。相反,尽量使用持有Activity弱引用的静态内部类。
阅读更多
How to Leak a Context: Handlers & Inner Classes
android non-static内部类导致的内存泄露
【译】什么导致了Context泄露:Handler&内部类
非静态内部类可能导致的内存泄漏及其优化