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

查看下面一段代码

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值