Android 內存泄漏案例分析總結(Handler)

在Android開發開發中,操作不當很容易引起內存泄漏,這里主要記錄下平時遇到問題,包括:靜態變量(也包含集合)、非靜態的內部類、Handler、監聽器,尤其是 Handler 在開發中要格外的注意。

1、靜態變量

public class LeakActivityDemo extends Activity{
  private static TextView mTextView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextView = new TextView(this);
    mTextView.setText("demo");
  }
}

我這里這是一個例子,一般開發不會這么寫,上面的代碼在 Activity 結束的時候,mTextView 一直持有 this 引用,

導致整個 Activity 無法回收

解決:方法在 Activity 生命周期 onDestroy 時將 mTextView 置空,或者盡量少使用到靜態變量。

注意:在寫代碼時,要考慮到當前的變量是否持有當前 Activity 的引用,避免出現內存泄漏.

2、非靜態內部類

關于 Handler

public class LeakActivity extends Activity{

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mHandler.postDelayed(new Runnable() {

      @Override
      public void run() {

      }
    }, 1);
  }

  //提示:This Handler class should be static or leaks might occur (com.mvp.view.LeakActivity.1)
  private Handler mHandler = new Handler() {
      @Override public void handleMessage(Message msg) {
          super.handleMessage(msg);
      }
  };
}

在 Android 開發中,我們一般使用 Handler 處理異步操作,通常我們的代碼會像上面一樣實現,但是上面的代碼可能存在泄漏,

其實編譯器已經提示了警告,建議使用 static 靜態標示。

原因:

1、首先在 java 中,非靜態內部類會或者內部類會持有外部對象的引用,而靜態內部類不會持有外部類的引用;

2、和 Handler 機制有關,我們知道 Handler 和 Looper 是一起工作的,在一個 Activity 起來的時候,會幫我們創建一個

在主線程中使用的 Looper,用來處理所有的消息,當 Hanlder 初始化好以后,就會有一個消息發送到了 Looper 的消息隊列中,

而這個消息則包含了當前 Handler 的引用,這就是內存泄露的原因;

解決:

1、使用靜態內部類,如果在內部類中使用了 Activiy 則要使用 WeakReference(弱引用),并且需要注意判空。

2、還有Hanlder要在生命周期 ondestroy 時,取消該 Handler 對象的 Messag 和 Runnable。

例如:removeCallbacks(Runnable r)、removeMessages(int what)、mHandler.removeCallbacksAndMessages(null);

private final MyHandler mHandler = new MyHandler(this);

  public static class MyHandler extends Handler {
      private final WeakReference<LeakActivity> mWeakActivity;

      public MyHandler(LeakActivity context) {
        mWeakActivity = new WeakReference<LeakActivity>(context);
      }

      @Override public void handleMessage(Message msg) {
          super.handleMessage(msg);
          if (mWeakActivity.get() != null) {
            mWeakActivity.get().todo();
      }
      }
  }

  public void todo(){
    //todo
  }

關于 Runnable

常常會這么寫,會存在很大的內存泄露問題,內部類會持有外部對象的引用,如果我們 run 方法操作了 UI 等,或者 使用了 postDelayed 方法,很容易造成內存泄露問題。

mHandler.post(new Runnable() {

    @Override
    public void run() {
      ..
    }
});

解決方法:使用靜態的Runnable 和 WeakReference 來解決引用問題,使用 WeakReference 要注意判斷,因為可能隨時會被回收。

private static final class ItemRunnble implements Runnable {

  private final WeakReference<Item> mWeakReference;

  public ProgressBarRunnble(Item f) {
    mWeakReference = new WeakReference<Item>(f);
  }

  @Override
  public void run() {
    Item item = mWeakReference.get();
    if (item != null) {
      ...
    }
  }
}

mHandler.post(new ItemRunnble(mItem))

3、非靜態內部類生成的靜態變量

private static MyClass myClass;
  private class MyClass {

  }

..

myClass = new MyClass();

這種是兩者的綜合體,只要是非靜態內部類就會持有外部類的引用,如果外部類正好是 Activity ,那么會導致 Activity 無法回收;

處理方式和第一種很像,記得釋放

總結

靜態變量最容易出現內存泄露,這些靜態變量的生命周期和應用程序一致,所有的對象 Object 也不能被釋放,

而集合類如果不 remove 添加的 Object,則會永遠持有這些 Ojbect,也會導致無法釋放。

監聽器是一種使用方式,Android 中比較常見的是 Listener,Observer 等,

一般被監聽者的生命周期要比監聽者的生命周期長,當監聽者本身不被使用,但又沒有移除被監聽者對其的引用時就會造成內存無法釋放。

數據庫在不使用時也沒有關閉,那么這部分內存也就無法回收。

以下列舉注意情況:

  • 遵守生命周期,創建時創建,銷毀時記得回收
  • Bitmip 和 Drawable 記得手動回收
  • 減少靜態變量的使用
  • 自定義靜態 Handler,和 Handler 回收
  • 使用 Application Context,少使用 Activity Context
  • 監聽器不使用的時候記得釋放
  • 如果循環中使用較多臨時變量,當不使用時及時釋放

參考文章

 

來自:http://www.jianshu.com/p/04c94470c50f

 

 本文由用戶 qashili 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!