Handler作为Android中一个消息传递的工具,使用非常频繁。不论是应用层开发,还是系统库件如AsyncTask的封装,都或多或少地使用了它。然而,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();
  }
} 

  这个用法的错误在于,它创造了一条非常长的引用链。Handler作为Activity的内部类,会自动持有Activity的引用,然后Handler通过postDelay发出一个Message,等待着它的回音,因为Message也会持有Handler的引用。Message会被放入MessageQueue中,等待被执行。归纳一下,我们可以看到以上的代码实现了如下一条引用链:  

    MessageQueue -----> Message -----> Handler -----> Activity

这条引用链非常长,并且MessageQueue的生命周期是Application级的。如果这条引用链不断,里面的任何一个对象,都将无法被回收。

 

解决方法:

在此提供两种解决思路

一、弱化Handler -----> Activity

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();
  }
}

  这个思路的核心即是通过静态类和弱引用,来弱化Handler -----> Activity。Handler变成静态类,这样它就只属于Activity的类,不属于Activity的对象。然后使Handler中以弱引用握持Activity,这样两个类之间的联系就可以弱化,Handle即使被Message引用,Activity也可以照常回收。

 

  这个思路的好处就是方法比较明确,比较稳定。并且在Activity的情况下,也不会有什么问题。但是如果我的Handle是存在于一个View。而每个View都需要一个Handler来控制它,此时用static类就是不适合的。那么我们只能另寻办法。

二、切断MessageQueue -----> Message

   MessageQueue -----> Message -----> Handler -----> Activity

  我们可以从MessageQueue -----> Message下手,考虑切断它们的引用联系。

  所幸Handler为我们提供了方法

    /**
     * Remove any pending posts of callbacks and sent messages whose
     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
     * all callbacks and messages will be removed.
     */
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

  使用这个方法,我们可以将此Handler在MessageQueue中的所有Message清除。同时,我们还要将Activity中所有的Runnable停止。也就是说,现在是我们需要手动清除Activity的所有强引用。这样也可以防止内存泄露。这个方法对结构的破怀性小,好处显而易见。但坏处也同样明显,如果我们在各种引用中漏掉了某一项没有清除,那前面的所有动作都前功尽弃了。因此,这个方法难度比较大,难以保证其正确性。

 

理解有限,欢迎拍砖。

 

Done

 

posted on 2016-03-21 19:56  Fishbonell  阅读(248)  评论(0编辑  收藏  举报