Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android內存洩漏終極解決篇(下)

Android內存洩漏終極解決篇(下)

編輯:關於Android編程

一、概述

在 Android內存洩漏終極解決篇(上)中我們介紹了如何檢查一個App是否存在內存洩漏的問題,本篇將總結典型的內存洩漏的代碼,並給出對應的解決方案。內存洩漏的主要問題可以分為以下幾種類型:

  • 靜態變量引起的內存洩漏
  • 非靜態內部類引起的內存洩漏
  • 資源未關閉引起的內存洩漏

二、靜態變量引起的內存洩漏

在java中靜態變量的生命周期是在類加載時開始,類卸載時結束。換句話說,在android中其生命周期是在進程啟動時開始,進程死亡時結束。所以在程序的運行期間,如果進程沒有被殺死,靜態變量就會一直存在,不會被回收掉。如果靜態變量強引用了某個Activity中變量,那麼這個Activity就同樣也不會被釋放,即便是該Activity執行了onDestroy(不要將執行onDestroy和被回收劃等號)。這類問題的解決方案為:1.尋找與該靜態變量生命周期差不多的替代對象。2.若找不到,將強引用方式改成弱引用。比較典型的例子如下:

單例引起的Context內存洩漏

public class IMManager {
  private Context context;
  private static IMManager mInstance;

  public static IMManager getInstance(Context context) {
    if (mInstance == null) {
      synchronized (IMManager.class) {
        if (mInstance == null)
          mInstance = new IMManager(context);
      }
    }
    return mInstance;
  }

  private IMManager(Context context) {
    this.context = context;
  }

}

當調用getInstance時,如果傳入的context是Activity的context。只要這個單例沒有被釋放,這個Activity也不會被釋放。

解決方案
傳入Application的context,因為Application的context的生命周期比Activity長,可以理解為Application的context與單例的生命周期一樣長,傳入它是最合適的。

public class IMManager {
  private Context context;
  private static IMManager mInstance;

  public static IMManager getInstance(Context context) {
    if (mInstance == null) {
      synchronized (IMManager.class) {
        if (mInstance == null)
          //將傳入的context轉換成Application的context
          mInstance = new IMManager(context.getApplicationContext());
      }
    }
    return mInstance;
  }

  private IMManager(Context context) {
    this.context = context;
  }

}

三、非靜態內部類引起的內存洩漏

在java中,創建一個非靜態的內部類實例,就會引用它的外圍實例。如果這個非靜態內部類實例做了一些耗時的操作,就會造成外圍對象不會被回收,從而導致內存洩漏。這類問題的解決方案為:1.將內部類變成靜態內部類 2.如果有強引用Activity中的屬性,則將該屬性的引用方式改為弱引用。3.在業務允許的情況下,當Activity執行onDestory時,結束這些耗時任務。

內部線程造成的內存洩漏

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    test();
  }

  public void test() {
    //匿名內部類會引用其外圍實例LeakAty.this,所以會導致內存洩漏
    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
  }

解決方案
將非靜態匿名內部類修改為靜態匿名內部類

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    test();
  }
  //加上static,變成靜態匿名內部類
  public static void test() {
    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
}

Handler引起的內存洩漏

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    fetchData();

  }

  private Handler mHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
      switch (msg.what) {
      case 0:
        // 刷新數據
        break;
      default:
        break;
      }

    };
  };

  private void fetchData() {
    //獲取數據
    mHandler.sendEmptyMessage(0);
  }
}

mHandler 為匿名內部類實例,會引用外圍對象LeakAty.this,如果該Handler在Activity退出時依然還有消息需要處理,那麼這個Activity就不會被回收。

解決方案

public class LeakAty extends Activity {
  private TextView tvResult;
  private MyHandler handler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    tvResult = (TextView) findViewById(R.id.tvResult);
    handler = new MyHandler(this);
    fetchData();

  }
  //第一步,將Handler改成靜態內部類。
  private static class MyHandler extends Handler {
    //第二步,將需要引用Activity的地方,改成弱引用。
    private WeakReference<LeakAty> atyInstance;
    public MyHandler(LeakAty aty) {
      this.atyInstance = new WeakReference<LeakAty>(aty);
    }

    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      LeakAty aty = atyInstance == null ? null : atyInstance.get();
      //如果Activity被釋放回收了,則不處理這些消息
      if (aty == null||aty.isFinishing()) {
        return;
      }
      aty.tvResult.setText("fetch data success");
    }
  }

  private void fetchData() {
    // 獲取數據
    handler.sendEmptyMessage(0);
  }

  @Override
  protected void onDestroy() {
    //第三步,在Activity退出的時候移除回調
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
  }
}

四、資源未關閉引起的內存洩漏

當使用了BraodcastReceiver、Cursor、Bitmap等資源時,當不需要使用時,需要及時釋放掉,若沒有釋放,則會引起內存洩漏。

綜上所述,內存洩漏的主要情況為上面的三大類型,最終歸結為一點,就是資源在不需要的時候沒有被釋放掉。所以在編碼的過程中要注意這些細節,提高程序的性能。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved