android避免記憶體泄露

來源:互聯網
上載者:User

1、   資料庫的cursor沒有關閉
2、 構造adapter沒有使用緩衝contentview
   衍生的listview最佳化問題:減少建立View的對象,充分使用contentview,可以使用靜態類來處理最佳化getView的過程
3、Bitmap對象不使用時採用recycle()釋放記憶體
4、Activity中的對象生命週期大於Activity
調式方法:DDMS->HEAPSIZE->adtaobject->total size
 
 
Android應用程式被限制在16MB的堆上運行,至少在T-Mobile G1上是這樣。對於手機來說,這是很大的記憶體了;但對於一些開發人員來說,這算是較小的了。即使你不打算使用掉所有的記憶體,但是,你也應該儘可能少地使用記憶體,來確保其它應用程式得以運行。Android在記憶體中保留更多的應用程式,對於使用者來說,程式間切換就能更快。作為我(英文作者)工作的一部分,我調查了Android應用程式的記憶體泄露問題,並發現這些記憶體泄露大多數都是由於相同的錯誤導致的,即:對Context擁有較長時間的引用。

在Android上,Context常用於許多操作,更多的時候是載入和訪問資源。這就是為什麼所有的Widget在它們的建構函式裡接受一個Context的參數。在一個正常的Android應用程式裡,你會看到兩種Context類型,Activity和Application。而一般在需要一個Context的類和方法裡,往往傳入的是第一種:
Java代碼 
@Override   
   
protected void onCreate(Bundle state) {   
   
  super.onCreate(state);   
   
    
   
  TextView label = new TextView(this);   
   
  label.setText("Leaks are bad");   
   
    
   
  setContentView(label);   
   
}   
 
這意味著,View擁有對整個Activity的引用以及Activity自身擁有的所有內容;一般是整個的View層次和它的所有資源。因此,如果你“泄露”了Context(“泄露”指你保留了一個引用,阻止了GC的記憶體回收),你將泄露很多的記憶體。如果你不夠仔細的話,很容易就能泄露一個Activity。

當螢幕的方向發生改變時,一般系統會銷毀當前的Activity並建立一個新的,並儲存它的狀態。當系統這樣做時,Android會從資源中重新載入應用程式的UI。假設你寫的應用程式擁有大的位元影像,而你又不想在每次旋轉時重新載入它。這裡有最簡單的方式,那就是在一個靜態欄位裡進行儲存:
Java代碼 
private static Drawable sBackground;   
    
@Override   
protected void onCreate(Bundle state) {   
  super.onCreate(state);   
    
  TextView label = new TextView(this);   
  label.setText("Leaks are bad");   
    
  if (sBackground == null) {   
    sBackground = getDrawable(R.drawable.large_bitmap);   
  }   
  label.setBackgroundDrawable(sBackground);   
    
  setContentView(label);   
}   
 
這段代碼效率很快,但同時又是極其錯誤的;在第一次螢幕方向切換時它泄露了一開始建立的Activity。當一個Drawable附加到一個View上時,View會將其作為一個callback設定到Drawable上。上述的程式碼片段,意味著Drawable擁有一個TextView的引用,而TextView又擁有Activity(Context類型)的引用,換句話說,Drawable擁有了更多的對象引用(依賴於你的代碼)。

這是最容易泄露Context的例子之一,你可以看看Home Screen原始碼裡是如何處理的(搜尋unbindDrawables()方法):當Activity銷毀時,設定儲存的Drawable的callback為null。有趣的是,還有很多一連串的Context泄露情況,並且是非常糟糕的。這些情況會使得應用程式很快耗盡記憶體。

這裡,有兩種簡單的方式可以避免與Context相關的記憶體泄露。最顯而易見的一種方式是避免將Context超出它自己的範圍。上面的例子代碼給出的靜態引用,還有內部類和它們對外部類的隱式引用也是很危險的。第二種解決方案是使用Application這種Context類型。這種Context擁有和應用程式一樣長的生命週期,並且不依賴Activity的生命週期。如果你打算儲存一個長時間的對象,並且其需要一個Context,記得使用Application對象。你可以通過調用Context.getApplicationContext()或Activity.getApplication()輕鬆得到Application對象。

概括一下,避免Context相關的記憶體泄露,記住以下事情:

   不要保留對Context-Activity長時間的引用(對Activity的引用的時候,必須確保擁有和Activity一樣的生命週期)

   嘗試使用Context-Application來替代Context-Activity

   如果你不想控制內部類的生命週期,應避免在Activity中使用非靜態內部類,而應該使用靜態內部類,並在其中建立一個對Activity的弱引用。這種情況的解決辦法是使用一個靜態內部類,其中擁有對外部類的WeakReference,如同ViewRoot和它的Winner類那樣

   GC(記憶體回收)不能解決記憶體泄露問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.