码迷,mamicode.com
首页 > 其他好文 > 详细

The handler class should be static or leaks might occur原因及解决方法

时间:2015-07-12 16:59:53      阅读:378      评论:0      收藏:0      [点我收藏+]

标签:

翻译自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

The handler class should be static or leaks might occur原因及解决方法

标签:

原文地址:http://www.cnblogs.com/zoejiaen/p/4580572.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!