先思考下面一个代码片段
publicclass SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
publicvoid handleMessage(Message msg) {
// ...
}
}
}
虽然不容易发现,但上面的代码会导致大量的内存泄漏。如果是在Eclipse中编写代码的话Android Lint会警告:
In Android,Handlers should be static or leaks might occur。但是内存泄漏到底是在哪发生的和到底是怎么发生的?现在就让我们来确定问题的根源,首先我们知道的是:
1、当一个Android应用启动时,Android平台框架会为应用的UI主线程创建一个Looper对象。Looper简单的实现了一个消息队列,采用队列的方式处理一个个消息。所有主要的应用程序框架中的事件都包含在消息对象中,所有这些事件消息都加入到Looper的消息队列中,一个接着一个的处理掉。并且UI主线程的Looper对象贯穿于应用程序的整个生命周期。
2、当一个Handler对象在UI主线程中经过实例化后,就与Looper消息队列相关联了。当一个消息发布进消息队列中时,Looper对象会持有一个Handler对象,以便于当Loope处理此消息时,Android平台框架能够调用Handler对象的
Handler#handleMessage(Message)
方法来进行处理
3、在Java当中,非静态的和匿名的内部类会隐式的持有它们外部类的一个引用
那么到底在哪里内存泄漏了呢?虽然非常的隐秘微妙,但是咋们不妨来看看下面一段代码
publicclass SampleActivity extends Activity {
privatefinal Handler mLeakyHandler = new Handler() {
@Override
publicvoid handleMessage(Message msg) {
// ...
}
}
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(newRunnable() {
@Override
publicvoid run() { }
},60 * 10 * 1000);
// Go back to the previous Activity.
finish();
}
}
当这个Activity结束销毁时,推迟的处理的消息依然会存在于消息队列中10分钟直到被处理掉。消息对象持有Activity的Handler对象的引用,而Handler对象持有一个隐式的外部类的引用(在上面的例子中是SampleActivity)。这个Activity引用会一直存在直到该消息被处理掉,因此阻止了垃圾回收器对Activity Context对象的回收,泄漏了所有的应用程序资源。上面的代码片中的第15行,非静态Runnable匿名类隐式持有Activity一个引用,而非静态匿名类对象Handler又隐式的持有Activity引用,所以Activity Context对象泄露了。
要解决此问题,我们可以使用一个外部的Handler子类(Activity Context依然可能会被泄漏,稍后会提到规避的方法)或者使用一个静态的Handler子类内部类。静态内部类并不会隐式的持有外部类的一个引用,所以不会泄漏Activity Context对象。但是如果你希望在静态内部类Handler子类中使用外部类Activity中的一些属性或方法,我们可以在静态内部类中持有一个外部类Activity的弱引用,这样Activity Context对象就不会泄漏了。当然我们也要把Runnable定义成一个静态的属性(静态的匿名类对象不会持有外部类的引用),如下代码所示
publicclass SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
privatestatic class MyHandler extends Handler {
privatefinal WeakReference<SampleActivity> mActivity;
publicMyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
publicvoid handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if(activity != null) {
// ...
}
}
}
privatefinal MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
privatestatic final Runnable sRunnable = new Runnable() {
@Override
publicvoid run() { }
};
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable,600000);
// Go back to the previous Activity.
finish();
}
}
虽然静态和非静态内部类之间的区别是相当微小的,但是每个Android开发者都应当知道。我们应当尽量避免在Activity中使用生命周期长于Activity生命周期的内部类,而应当使用静态内部类并且持有Activity的一个弱引用方式。