[Android]Context泄露之谜:Handle & 内部类

 

考虑下面代码:

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

尽管不是太明显,这段代码可能会导致严重的内存泄露。**Android Lint **会给出如下警告

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

可内存泄露是如何发生的呢?定位问题需要先列出我们已知的东西:

  • Android应用第一次启动时, 系统会在主线程创建Looper对象,Looper实现了一个简单的消息队列,循环处理Message。所有主要的应用层事件(例如Activity的生命周期方法回调、Button点击事件等等)都会包含在Message里,系统会把Message添加到Looper中,然后Looper进行消息循环。主线程的Looper会存在整个应用的生命周期间。

  • 当主线程创建Handler对象,会与消息队列Looepr绑定,被分发到消息队列的Message会持有Handler的引用,以便系统在Looper处理到该Message时能调用Handle#handlerMessage(Message)方法。

  • 在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生命周期结束后,延迟的Message在被处理之前会继续停留在主线程中10分钟。该Message持有该ActivityHandler
的引用,而Handler持有外部类(SampleActivity)的隐式引用。该引用会继续存在直到Message被处理。所以阻止了Activity
Context的回收,泄漏了所有应用的resources。注意上述代码第15行。非静态类和匿名内部类都会持有外部类的隐式引用,导致了Context泄漏。

为了解决这个问题,可以用新建一个Handler的静态子类。静态内部类不会持有外部类的隐式引用。因此不会导致内存泄漏。

如果你需要在Handler子类中调用外部类的方法,可以让Handler持有一个ActivityWeakReference。为了防止内存泄漏,我们新建一个静态的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();
  }
}

静态内部类和非静态内部类的区别很微妙,但每一个Android开发者都应该注意到这个标准,避免在Activity中使用非静态内部类,如果该类的实例会存在在Activity的生命周期之外。必须使用静态内部类持有一个外部类的弱引用替代。



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值