Android效能最佳化 - 避免記憶體泄露

來源:互聯網
上載者:User

Android效能最佳化 - 避免記憶體泄露

以前在網上也看過類似的譯文,但也忘得差不多了. 直至最近在官網再次看到原文, 雖是09年的文章, 略顯久遠, 但再看一次還是覺得總結很好. 於是決定翻譯下來, 順便鞏固自己的相關知識.

 

原文連結

 

安卓應用在大多數機型( 針對以前的比較舊的機型, 現在的手機配置越來越高, 可分配的運行記憶體也相應會比以前提高) 會得到16MB的應用記憶體. 即使你不打算真的使用這全部的記憶體, 你也應該儘可能的使用更少的記憶體, 以免因為佔用記憶體過大而進程被系統殺死. Android手機記憶體中(短暫)保留的應用更多, 那麼使用者在這些被保留的應用間互相切換時就會更快(得到響應). 工作中, 碰到記憶體泄露問題是會碰到的情況, 並且這種情況大多數都是因為時間過長的( 對象,資源等 )引用存在於應用上下文(Context)中.

 

在Android中, 一個Context被用於很多的操作中, 但大多數是用於載入和使用資源, 這也是為什麼所有組件(Widgets)在構造器中都需要接收一個Context參數. 在一般的Android應用中, 你通常會擁有2種Context, Activity和 Applicatin. 這也通常是開發人員首先要傳到類和方法中的Context:

 

@Overrideprotected void onCreate(Bundle state) {  super.onCreate(state);    TextView label = new TextView(this);  label.setText("Leaks are bad");    setContentView(label);}

 

 

這意味著, 視圖(Views)有一個在整個Activity中的引用, 也因此該Activity會(緊緊)保持這些Views: 通常是整個View所有層級的父View和子View,以及這些View所用到的資源. 因此, 如果你泄露(leak)了上下文(Context).(這裡的"泄露" 是指你保持了一個避免被GC - 記憶體回收機制回收 的引用 ), 逐漸的會變成泄露了很多記憶體. 這樣下去, 如果你稍不注意, 泄露一整個Activity的記憶體是很容易發生的事情.

 

當螢幕橫豎屏切換, 系統會預設銷毀當前Activity, 然後會根據舊Activity被銷毀前被儲存的狀態(要開發人員在銷毀前手動儲存狀態才有效)去建立一個新的Activity. 這樣, Android將會重載應用的UI布局和資源的. 現在你不想應用在每一個方向(豎屏, 橫屏)都載入一個大的位元影像(bitmap). 那麼保持這個位元影像, 不在每次倒置螢幕時都去重載它的最簡單的方法就是將它設為靜態(static):

private static Drawable sBackground;@Overrideprotected 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 會被設定一個Drawable的回調. 在上面的代碼塊中, 這個意味著drawable在TextView中有一個應用, 而這個TextView本身在Activity(上下文)中又有一個引用. 類似這樣, Activity就保持著各種資源的引用(當然這取決於你的代碼).

 

這是其中一個最簡單的泄露上下文(Context)記憶體的例子, 你能在這裡( 在主畫面源碼 Home screen's source code 尋找 unbindDrawables()方法)看到它是怎麼工作的. 當Activity被銷毀時, 將(引用的)儲存的drawable回調為null. 有趣的是, 在某些你建立了資源間互相引用的"鏈"的情況下這麼做是不好的, 反而使應用更快的耗盡記憶體.

 

這裡有2個簡單的方法去避免相關的上下文記憶體泄露. 最顯著的一個方法是避免資源脫離自身所在的上下文範圍(而存在). 上面的例子展示了靜態引用的案例, 內部類和該類包含的引用對其他類可能是同樣的危險. 第二個解決方案是是適用 Application 這個內容物件. Application 會一直在應用運行時存在, 並且不會像Activity受其生命週期影響. 如果你打算保持一個長期使用的對象, 就需要一個這樣的上下文(Context). 你可以輕易的通過調用 Context.getApplicationContext() 和 Activity.getApplication() 方法來獲得它.

 

總結, 為了避免相關上下文泄露記憶體, 請記得以下的幾點:

 

  • 在Activity中不要保持長時間的引用(引用應該跟隨同樣的Activity生命週期一樣, Activity銷毀, 引用也置空)
  • (對於某些需要長時間保持的引用)嘗試使用Application代替Activity
  • 如果你不能控制類的生命週期, 則避免在Activity中使用非靜態內部類. 最好在Activity中使用靜態內部類和弱引用. 這些問題的解決方案是一個被外部類被使用的內部類應當使用 WeakReference 弱引用(相關樣本可看 ViewRoot )
  • 系統的記憶體回收機制並不能保證記憶體不泄露

     

     

     

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.