Handle & Inner Classes 如何避免内存泄漏

来源:互联网 发布:mac 双系统丢失 编辑:程序博客网 时间:2024/06/05 09:24

查看下面一段代码

public class SampleActivity extends Activity {  private final Handler mLeakyHandler = new Handler() {    @Override    public void handleMessage(Message msg) {      // ...     }  };}

上面一段代码看起来很正常,但这段代码确实能造成大量的内存泄漏。并且,Android Lint(Android studio 编译器的提示) 将会给你下面的警告:

In Android, Handler classes should be static or leaks might occur.

但泄漏到底在哪里,何时发生? 我们就可以用我们所知道的来确定问题的根源:

1.Android 应用程序首次启动时,framework将会为application’s main thread 创建一个looper对象。looper实现了一个简单的message队列,循环的处理message对象。所有的主要application framework事件(例如 activity生命周期方法的调用, button的点击事件, 等等) 都包含在Message对象中,message 被添加到looper的message队列中并且逐个处理。

2.当一个handler在main thread中实例化,它将会和looper’s 消息队列关联。当handler发送一个message到消息队列中,该message对象将会持有handler的引用. 所以,当looper处理该消息的时候,framework 可以调用handler#handler(message)方法

3.在Java中,非静态内部和匿名类保留对其外部类的隐含引用。但是,静态内部类将不会。

那么内存泄漏究竟在哪里呢?这很微妙,我们以下面的代码为例:

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被销毁,延迟消息在被处理之前会继续在主线程的队列中存活10分钟。消息对象将会持有handler引用,而且handler也持有一个隐含的引用外部类(在这个例子中是SampleActivity)。这个引用直到消息被处理之前将一直保留。注意,在匿名 Runnable 类中的15行也是如此。非静态的匿名类实例持有一个他们的外部类的隐含引用,所以,context将会泄漏。

要解决这个问题,需要将继承handler的子类创建成一个新的文件, 或者使用静态内部类来代替。静态内部类不会持有他们的外部类的隐含引用,所以,activity将不会泄漏。如果你需要在handler内部调用外部activity的方法,可以在handler持有一个WeakReference 的activity,那么你讲不会意外地泄漏context。为了修复当我们实例化一个匿名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 pevious Activity.    finish();  }}

静态类和非静态类的区别是微妙的,但是,这是每一个android开发人员需要知道的。
最关键的是什么?如果一个内部类能够存活的比activity的生命周期还长,应该避免在activity使用非静态内部类。因此,宁可选择静态内部类并在里面持有一个弱引用的activity。

原文链接:http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

原创粉丝点击