避免Android內存泄露

Android的應用被限制爲最多佔用16m的內存,至少在T-Mobile G1上是這樣的(當然現在已經有幾百兆的內存可以用了——譯者注)。它包括電話本身佔用的和開發者可以使用的兩部分。即使你沒有佔用全部內存的打算,你也應該儘量少的使用內存,以免別的應用在運行的時候關閉你的應用。Android能在內存中保持的應用越多,用戶在切換應用的時候就越快。作爲我的一項工作,我仔細研究了Android應用的內存泄露問題,大多數情況下它們是由同一個錯誤引起的,那就是對一個上下文(Context)保持了長時間的引用。

    在Android中,上下文(Context)被用作很多操作中,但是大部分是載入和訪問資源。這就是所有的widget都會在它們的構造函數中接受一個上下文(Context)參數。在一個合格的Android應用中,你通常能夠用到兩種上下文(Context):活動(Activity)和應用(Application)。活動(Activity)通常被傳遞給需要上下文(Context)參數的類或者方法:


  1. @Override  
  2. protected void onCreate(Bundle state) {  
  3.   super.onCreate(state);  
  4.    
  5.   TextView label = new TextView(this);  
  6.   label.setText("Leaks are bad");  
  7.    
  8.   setContentView(label);  
  9. }  


    這就意味着那個View有一個對整個活動(Activity)的引用並且對這個活動(Activity)中保持的所有對象有保持了引用;通常它們包括整個View的層次和它的所有資源。因此,如果你“泄露”了上下文(Context)(這裏“泄露”的意思是你保持了一個引用並且組織GC收集它),你將造成大量的內存泄露。如果你不夠小心的話,“泄露”一整個活動(Activity)是件非常簡單的事情。

    當屏幕的方向改變時系統會默認的銷燬當前的活動(Activity)並且創建一個新的並且保持了它的狀態。這樣的結果就是Android會從資源中重新載入應用的UI。現在想象一下,你寫了一個應用,有一個非常大的位圖,並且你並不想在每次旋轉時都重新載入。保留它並且每次旋轉不重新加載的最簡單的辦法就是把它保存在一個靜態字段上:

  1. private static Drawable sBackground;  
  2.    
  3. @Override  
  4. protected void onCreate(Bundle state) {  
  5.   super.onCreate(state);  
  6.    
  7.   TextView label = new TextView(this);  
  8.   label.setText("Leaks are bad");  
  9.    
  10.   if (sBackground == null) {  
  11.     sBackground = getDrawable(R.drawable.large_bitmap);  
  12.   }  
  13.   label.setBackgroundDrawable(sBackground);  
  14.    
  15.   setContentView(label);  
  16. }  



    這段代碼非常快,同時也錯的夠離譜。它泄露了當第一次屏幕角度改變時創建的第一個活動(Activity)。當一個Drawable被附加到一個View,這個View被設置爲drawable的一個回調。在上面的代碼片斷中,這意味着這個Drawable對TextView有一個引用,同時這個TextView對Activity(Context對象)保持着引用,同時這個Activity對很多對象又有引用(這個多少還要看你的代碼了)。

    這個例子是造成Context泄露的最簡單的一個原因,你可以看一下我們在主屏幕源碼(查看unbindDrawables()方法)中是通過在Activity銷燬時設置保存過的Drawable的回調爲空來解決這個問題的。更爲有趣的是,你可以創建一個context泄露的鏈,當然這非常的糟糕。它們可以讓你飛快的用光所有的內存。

    有兩種簡單的方法可以避免與context相關的內存泄露。最明顯的一個就是避免在context的自身的範圍外使用它。上面的例子展示了在類內部的一個靜態的引用和它們對外部類的間接引用是非常危險的。第二個解決方案就是使用Application Context。這個context會伴隨你的應用而存在,並且不依賴Activity的的生命週期。如果你計劃保持一個需要context的長生命週期的對象,請記得考慮Application對象。你可以非常方便的通過調用Context.getApplicationContext() 或者 Activity.getApplication()獲取它。

總之,爲了避免涉及到context的內存泄露,請記住如下幾點:

  1. 不要對一個Activity Context保持長生命週期的引用(一個對Activity的引用應該與Activity自身的生命週期相同)
  2. 嘗試使用應用上下文(context-application)代替活動上下文(context-activity)
  3. 如果你不能控制它們的生命週期,在活動(Activity)中避免使用不是靜態的內部類,使用靜態類並且使用弱引用到活動(Activity)的內部。對於這個問題的解決方法是使用靜態的內部類與一個弱引用(WeakReference)的外部類。就像ViewRoot和它的W內部類那麼實現的。
  4. 垃圾回收器對於內存泄露來說並不是百分百保險的。

原文地址:http://blog.csdn.net/xyz_lmn/article/details/7108011

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