翻译自http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
在主线程中使用Handler对象,比如下面的代码
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } }
这段代码会产生隐秘的内存溢出的错误,Android Lint会给出羡慕的警告:
The handler class should be static or leaks might occur.
但是究竟哪一部分内存会在什么情况下溢出呢?
要解决这一问题需要了解一下安卓系统背景知识:
1. 当一个Android应用启动的时候,Android系统为这个应用的主线程创建一个Looper对象。Looper对象实现了简单的消息队列(message queue),依次处理循环(Looper)中的消息。所有的主要应用事件(比如Activity的生命周期,按钮的点击等)都被包裹为一个消息(Message)实体,被添加到Looper的消息队列中依次处理。主线程的Looper在应用的整个生命周期中都存在。
2.当一个Handler对象在主线程中初始化时,它被关联到Looper的消息队列中。在Handler的sendMessage()方法被调用的时候,一个Message对象会被发往Looper的消息队列中,被发送到消息队列中的Message将会持有Handler的引用,然后系统才能在Looper处理Message时调用Handler对象的handleMessage(Message)方法。
3.在Java中,非静态(non-static)内部和匿名类将会持有外部类的引用。相反,静态的内部类不会持有外部类的引用。
更多Looper,Handler相关的知识可以在文后链接的博客中找到。
了解了这些背景之后我们来看一下内存在什么情况下溢出,首先看下面一段代码:
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
当Activity结束的时候,被延迟的Message会继续存活在主线程中10分钟,直到它被处理了。这个Message持有Activity的Handler对象的引用,同时Handler是Activity的非静态(non-static)匿名内部类,所以Handler持有外部类也就是Activity的引用。这样的引用关系会阻止Activity被Java GC回收,释放系统持有的资源,直到Message被处理了。另外杨的引用对于匿名Runnable()对象也存在。
解决这个问题的方法可以有:在一个新的文件中继承Handler类,或者使用一个静态内部类。静态内部类不会持有外部类的引用,所以Activity不会被泄露。如果需要在Handler中调用外部类(此处为Activiity)的方法,可以使Handler拥有Activity的弱引用(WeakReference),这样就不会意外地泄露Activity。
为了解决实例化内部匿名Runnable类时导致的内存泄漏的问题,我们可以使内部匿名Runnable类变为外部类的静态对象。
public class SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ 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); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
在Activity中实例化内部类时,如果内部类可以在Activity的生命周期之外继续存活于哦,那么这样的内部类不能为非静态的。这种情况应该使用静态内部来并且持有外部类对象的弱引用。
相关链接:
http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html