記憶體優化是Android效能優化的重點內容,一般來說,談及效能優化,肯定避不開記憶體優化。雖然現在手機記憶體都很大,但並不意味著我們的App在使用記憶體時能“為所欲為”。這篇就簡單地總結一下關於記憶體優化要注意的一些事項。
程式級別的記憶體策略
我們知道,Android 系統是一個基於 Linux 的開源系統,使用的是 Dalvik / Android Runtime 作為對應的虛擬機器來執行程式碼。底層記憶體分配機制在這裡就不詳述了,只看 Application Framework 這一層。
首先,Android 中的記憶體分配由 ActivityManagerService
統一管理。這是一個很重要的類,除了記憶體管理之外,它還管理了 Activity
生命週期,啟動行為,訊息派發等功能。當使用者點選應用的啟動圖示時, ActivityManagerService
會請求系統建立一個程式,當程式建立完成之後,繫結給 ActivityManagerService
,這時才會開始 App 本身的生命週期。
而關於記憶體回收這塊,Application Framework 給建立的程式規定了五個的回收型別優先順序,即從最先被回收到最後被回收,分別是:
- Empty process 空程式
- Background process 後臺程式
- Service process 服務程式
- Visible process 可見程式
- Foreground process 前臺程式
然後在 ActivityManagerService
中,會對所有程式進行評分,並將評分同步到 Linux 核心中,最終由核心來執行記憶體回收
物件級別的記憶體策略
關於物件級別的記憶體管理策略,由於是應用程式自動管理記憶體分配以及記憶體回收(Java GC),能操作的有限,但是基本的概念還是要了解的,因為當你知道物件/變數在記憶體中是如何分配,以及分配到堆/棧/方法區的時候,在寫程式碼的時候就會有意規避一些問題,達到記憶體優化的效果。
Android常見記憶體問題
記憶體洩漏
記憶體洩漏 Memory Leak,指的是記憶體申請並使用完畢以後,系統因為某些原因無法回收該記憶體塊的情況。其實質也是長生命週期物件持有短生命週期的物件,導致短生命週期物件需要被回收時,由於長生命週期的物件持有無法被回收的現象。
在Android中,以下幾個場景容易出現記憶體洩漏。
單例物件使用了非全域性的 Context
這個是初學者容易犯的錯誤,在構造一個單例物件時,往往需要使用 Context 物件來初始化建構函式,並持有一個 Context物件,這時如果傳入一個非全域性的 Context ,會導致該 Context物件在 GC 時無法回收,造成記憶體洩漏。
解決方案:單例使用 ApplicationContext ,這個物件的生命週期是整個應用的生命週期,不會導致洩漏
匿名內部類 / 非靜態內部類 / 非同步執行緒 / Handler 持有外部類的引用
這也是常見的記憶體洩漏易於出現的場景。舉例來說,我們現在經常使用 Rxjava + Retrofit + OkHttp 來構建網路請求。有時候會碰到網速過慢導致網路請求返回慢或者超時的情況。這時如果我們關閉了當前頁面,網路請求結果仍然會被觀察者接受並重新整理UI。這時本來要被回收的UI物件由於被觀察者持有,無法回收,就導致了記憶體洩漏。
解決方案:
- 使用靜態內部類,並在需要的時候引入外部類,而不是直接在建構函式中引用
- 使用弱引用 WeakReference 來引用外部類例項(掌握Java的四種引用方式)
資源未關閉導致無法回收
這也是經常被提到的點,註冊並使用後,忘記在生命週期結束時解綁,導致無法回收。
解決方案:BroadcastReceiver、ContentObserver、File、Bitmap、Timer、EventBus 等都是需要解綁或者清空的,要養成直覺
WebView不要在佈局中定義
這個是網路上一篇文章裡看到的,我想現在應該沒有多少人會在佈局裡定義 WebView 吧(還有 Fragment )
解決方案:在程式碼中構造WebView物件,建立時上下文使用 ApplicationContext
記憶體溢位
記憶體溢位 Out Of Memory ,是指應用的記憶體申請超出了當前所能申請的最大記憶體容量,導致應用出現一系列問題甚至被系統殺掉程式。在 Android 中出現記憶體溢位,主要是因為以下原因引起。
使用 Bitmap 並且未優化
Bitmap 是產生記憶體溢位的大戶,如果沒有經過任何優化,直接載入一個 Bitmap 的話,會導致該物件吃掉大量記憶體。
解決方案:
- 載入圖片之前先計算出合適的縮放比例,按比例縮放。
- 選擇合適的解碼格式。不同的格式,記憶體佔用在很大差異。
- Bitmap 不用時要及時回收,呼叫
recycle
方法。
短時間建立大量物件
這個常見於列表元件的載入。載入列表時如果不優化,同一時間內建立了過多物件,就會造成記憶體溢位。
解決方案:
- 使用按需載入的方式載入內容(上拉載入更多)
- 常用物件做到儘量複用,並快取常用物件。
其他記憶體溢位的場景和解決方案
- 捕獲 OOM 異常,避免因為記憶體溢位而導致崩潰
- 使用優化的資料容器,合適的記憶體容器可以大幅度增加記憶體效能
- 使用 zipalign 對齊可以一定量優化記憶體效能(Google 官方推薦)
- 使用成熟的第三方框架,例如使用 Glide 處理圖片