Handler的內存泄露分析

  Handler做爲Android中一個消息傳遞的工具,使用很是頻繁。不管是應用層開發,仍是系統庫件如AsyncTask的封裝,都或多或少地使用了它。然而,Handler的危險性也是很是大的,使用起來稍有不慎就會引發內存泄露。ide

 

  泄露來源分析:工具

  常見錯誤用法: post

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中,等待被執行。概括一下,咱們能夠看到以上的代碼實現了以下一條引用鏈:  this

    MessageQueue -----> Message -----> Handler -----> Activityspa

這條引用鏈很是長,而且MessageQueue的生命週期是Application級的。若是這條引用鏈不斷,裏面的任何一個對象,都將沒法被回收。code

 

解決方法:對象

在此提供兩種解決思路blog

1、弱化Handler -----> Activitytoken

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類就是不適合的。那麼咱們只能另尋辦法。

2、切斷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

相關文章
相關標籤/搜索