Avoiding Memory Leaks

        我們大家都知道Android應用程序被限制在16MB的堆上運行,至少在T-Mobile G1上是這樣。對於手機來說,這是很大的內存了;但對於一些開發人員來說,這算是較小的了。即使我們不打算使用掉所有的內存,但是,我們也應該儘可能少地使用內存,來確保其它應用程序得以運行。Android在內存中保留更多的應用程序,對於用戶來說,程序間切換就能更快。我們調查了Android應用程序的內存泄露問題,並發現這些內存泄露大多數都是由於相同的錯誤導致的,即:對Context擁有較長時間的引用。

        在Android上,Context常用於許多操作,更多的時候是加載和訪問資源。這就是爲什麼所有的Widget在它們的構造函數裏接受一個Context的參數。在一個正常的Android應用程序裏,我們會看到兩種Context類型,Activity和Application。而一般在需要一個Context的類和方法裏,往往傳入的是第一種:

  1. @Overrideprotected void onCreate(Bundle state) {    
  2. super.onCreate(state);      
  3. TextView label = new TextView(this);    
  4. label.setText("Leaks are bad");      
  5. setContentView(label);  

      這意味着,View擁有對整個Activity的引用以及Activity自身擁有的所有內容;一般是整個的View層次和它的所有資源。因此,如果我們“泄露”了Context(“泄露”指你保留了一個引用,阻止了GC的垃圾回收),我們將泄露很多的內存。如果我們不夠仔細的話,很容易就能泄露一個Activity。

       當屏幕的方向發生改變時,一般系統會銷燬當前的Activity並創建一個新的,並保存它的狀態。當系統這樣做時,Android會從資源中重新加載應用程序的UI。假設我們寫的應用程序擁有大的位圖,而我們又不想在每次旋轉時重新加載它。這裏有最簡單的方式,那就是在一個靜態的字段裏進行保存:

  1. private static Drawable sBackground;    
  2. @Overrideprotected void onCreate(Bundle state) {  super.onCreate(state);      
  3. TextView label = new TextView(this);    
  4. label.setText("Leaks are bad");      
  5. if (sBackground == null) {      
  6. sBackground = getDrawable(R.drawable.large_bitmap);   
  7.  }    
  8. label.setBackgroundDrawable(sBackground);    setContentView(label);  

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

In summary, to avoid context-related memory leaks, remember the following:

  • Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
  • Try using the context-application instead of a context-activity
  • Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
  • A garbage collector is not an insurance against memory leaks

     不要保留對Context-Activity長時間的引用(對Activity的引用的時候,必須確保擁有和Activity一樣的生命週期)  
     嘗試使用Context-Application來替代Context-Activity  
     如果你不想控制內部類的生命週期,應避免在Activity中使用非靜態的內部類,而應該使用靜態的內部類,並在其中創建一個對Activity的弱引用。這種情況的解決辦法是使用一個靜態的內部類,其中擁有對外部類的WeakReference,如同ViewRoot和它的Winner類那樣  
     GC(垃圾回收)不能解決內存泄露問題

常見的內存泄露的

1.數據庫沒有關閉Cursor

  1. try {  
  2.     Cursor c = queryCursor();  
  3.     int a = c.getInt(1);  
  4.     ......  
  5.     c.close();  
  6. catch (Exception e) {  
  7. }  
  8. //雖然表面看起來,Cursor.close()已經被調用,但若出現異常,將會跳過close(),
  9. //從而導致內存泄露

應加修改爲finally {     c.close();   }  

2.調用registerReceiver後未調用unregisterReceiver()

      在調用registerReceiver後,若未調用unregisterReceiver,其所佔的內存是相當大的。
而我們經常可以看到類似於如下的代碼:

  1. registerReceiver(new BroadcastReceiver() {  
  2.     ...  
  3. }, filter); ...  //這是個很嚴重的錯誤,因爲它會導致BroadcastReceiver不會被unregister而導致內存泄露。

 3.未關閉InputStream/OutputStream

4. 構造adapter沒有使用緩存contentview

 衍生的listview優化問題:減少創建View的對象,充分使用contentview,可以使用靜態類來處理優化getView的過程

5. Bitmap對象不使用時採用recycle()釋放內存

6. Activity中的對象生命週期大於Activity

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章