內存優化是Android性能優化的重點內容,通常來講,談及性能優化,確定避不開內存優化。雖然如今手機內存都很大,但並不意味着咱們的App在使用內存時能「隨心所欲」。這篇就簡單地總結一下關於內存優化要注意的一些事項。java
咱們知道,Android 系統是一個基於 Linux 的開源系統,使用的是 Dalvik / Android Runtime 做爲對應的虛擬機來執行代碼。底層內存分配機制在這裏就不詳述了,只看 Application Framework 這一層。緩存
首先,Android 中的內存分配由 ActivityManagerService
統一管理。這是一個很重要的類,除了內存管理以外,它還管理了 Activity
生命週期,啓動行爲,消息派發等功能。當用戶點擊應用的啓動圖標時, ActivityManagerService
會請求系統建立一個進程,當進程建立完成以後,綁定給 ActivityManagerService
,這時纔會開始 App 自己的生命週期。性能優化
而關於內存回收這塊,Application Framework 給建立的進程規定了五個的回收類型優先級,即從最早被回收到最後被回收,分別是:網絡
而後在 ActivityManagerService
中,會對全部進程進行評分,並將評分同步到 Linux 內核中,最終由內核來執行內存回收框架
關於對象級別的內存管理策略,因爲是應用程序自動管理內存分配以及內存回收(Java GC),能操做的有限,可是基本的概念仍是要了解的,由於當你知道對象/變量在內存中是如何分配,以及分配到堆/棧/方法區的時候,在寫代碼的時候就會有意規避一些問題,達到內存優化的效果。異步
內存泄漏 Memory Leak,指的是內存申請並使用完畢之後,系統由於某些緣由沒法回收該內存塊的狀況。其實質也是長生命週期對象持有短生命週期的對象,致使短生命週期對象須要被回收時,因爲長生命週期的對象持有沒法被回收的現象。ide
在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
方法。
短期建立大量對象
這個常見於列表組件的加載。加載列表時若是不優化,同一時間內建立了過多對象,就會形成內存溢出。
解決方案:
- 使用按需加載的方式加載內容(上拉加載更多)
- 經常使用對象作到儘可能複用,並緩存經常使用對象。
其餘內存溢出的場景和解決方案