這多是最好的性能優化教程系列專欄
這多是最好的性能優化教程(一)
這多是最好的性能優化教程(二)
這多是最好的性能優化教程(三)android
內存泄漏歷來都是咱們老生常談的話題,不管是 Android Studio 自帶的內存泄漏分析工具仍是專業的 Eclipse MAT 抑或是備受青睞的第三方插件 LeakCanary,都爲咱們的內存泄漏檢測提供了便利。若是從根源上解決內存泄漏,內存優化必不可少。因此本章節咱們參考扔物線胡凱的內存優化策略,直接拿出一章節來談內存優化。git
內存優化基本能夠分爲下面幾個方面github
Enum 是 Java 中包含固定常量的數據類型,當須要知道預先定製的幾個值,這幾個值表示一些數據類,咱們均可以使用 Enum。咱們通常用 Enum 作一些編譯時檢查,以免傳入不合法的參數。數據庫
但 Enum 的每一個對象都是 Object,在 Android 官網上就早已明確指出應該在 Android 開發中避免使用 Enum,由於與靜態常量想必,它對內存的佔用是要大不少的。緩存
所以在實際開發中,我更加傾向於接口變量,由於接口會自動把成員變量設置爲 static 和 final 的,這一點能夠防止某些狀況下錯誤地添加新的常量,這也使得代碼看起來更加簡單和清晰。性能優化
前面第一節已經說過,咱們應該更加傾向於考慮使用 ArrayMap
和 SparseArray
而不是 HashMap
等傳統數據結果,前面已經用圖示演示了 HashMap
的簡要工做原理,相比起 Android 系統專門爲移動操做系統編寫的 ArrayMap
容器,在大多數狀況下,都顯示效率低下,更佔內存。一般的 HashMap
的實現方式更加消耗內存,由於它須要一個額外的實例對象來記錄 Mapping
操做。另外,SparseArray
更加高效在於他們避免了對 key
和 value
的 autobox
自動裝箱,而且避免了裝箱後的解箱。數據結構
在設計給到資源圖片的時候,咱們須要特別留意這張圖片是否存在能夠壓縮的空間,是否可使用一張更小的圖片。儘可能使用更小的圖片不只僅能夠減小內存的使用,還能夠避免出現大量的 InflationException
。假設有一張很大的圖片被 XML 文件直接引用,頗有可能在初始化視圖的時候就會由於內存不足而發生 InflationException
,這個問題的根本緣由實際上是發生了 OOM。app
Bitmap是一個極容易消耗內存的大胖子,減少建立出來的Bitmap的內存佔用是很重要的,一般來講有下面2個措施:ide
Android 系統中 float 類型的數據存取速度是 int 類型的一半,儘可能優先採用 int 類型。而一樣能做爲整數的代名詞,採用 int 替換 Integer 會讓你的內存開銷更小。函數
Android 系統自己內置了不少的資源,例如字符串 / 顏色 / 圖片 / 動畫 / 樣式以及簡單佈局等等,這些資源均可以在應用程序中直接引用。這樣作不只僅能夠減小應用程序的自身負重,減少 APK 的大小,另外還能夠必定程度上減小內存的開銷,複用性更好。可是也有必要留意 Android 系統的版本差別性,對那些不一樣系統版本上表現存在很大差別,不符合需求的狀況,仍是須要應用程序自身內置進去。
這個貌似沒啥好說的,太基礎了,並且咱們可能如今更加青睞於 RecyclerView
。
這個也特別基礎,咱們點到爲止。大概就是儘可能的採用 StringBuilder
/ StringBuffer
來替換咱們頻繁的字符串拼接。
當從已經存在的數據集中抽取出 String 的時候,嘗試返回原數據的 subString 對象,而不要建立一個重複的對象。
相似 onDraw() 等頻繁調用的方法,必定須要注意避免在這裏作建立對象的操做,由於他會迅速增長內存的使用,並且很容易引發頻繁的 gc,甚至是內存抖動。
內存對象的泄漏,會致使一些再也不使用的對象沒法及時釋放,這樣一方面佔用了寶貴的內存空間,很容易致使後續須要分配內存的時候,空閒空間不足而出現 OOM。顯然,這還使得每級 Generation 的內存區域可用空間變小,gc 就會更容易被觸發,容易出現內存抖動,從而引發性能問題。
一般來講,Activity 的泄漏是內存泄漏裏面最嚴重的問題,它佔用的內存多,影響面廣,咱們須要特別注意如下兩種狀況致使的 Activity 泄漏:
對於大部分非必須使用 Activity Context 的狀況(Dialog 的 Context 就必須是Activity Context),咱們均可以考慮使用 Application Context 而不是 Activity 的 Context,這樣能夠避免不經意的 Activity 泄露。
並且若是習慣 Glide 的童鞋可能會發現,Glide 須要傳遞的 Context 若是是 Activity 的 Context ,那麼在 Activity 被銷燬後還沒加載出來的話還會引起崩潰。因此,請在使用 Glide 或者 Toast 等的時候,直接傳遞 Application Context 吧。
在程序中咱們常常會進行查詢數據庫的操做,但時常會存在不當心使用 Cursor 以後沒有及時關閉的狀況。這些 Cursor 的泄露,反覆屢次出現的話會對內存管理產生很大的負面影響,咱們須要謹記對 Cursor 對象的及時關閉。
Android中 的 WebView 存在很大的兼容性問題,不只僅是 Android 系統版本的不一樣對 WebView 產生很大的差別,另外不一樣的廠商出貨的 ROM 裏面 WebView 也存在着很大的差別。更嚴重的是標準的 WebView 存在內存泄露的問題,看這裏。因此一般根治這個問題的辦法是爲 WebView 開啓另一個進程,經過 AIDL 與主進程進行通訊,WebView 所在的進程能夠根據業務的須要選擇合適的時機進行銷燬,從而達到內存的完整釋放。
雖然在大多數狀況下,咱們會對 Bitmap 增長緩存機制,可是在某些時候,部分 Bitmap 是須要及時回收的。例如臨時建立的某個相對比較大的 Bitmap 對象,在通過變換獲得新的 Bitmap 對象以後,應該儘快回收原始的 Bitmap,這樣可以更快釋放原始 Bitmap 所佔用的空間。
須要特別留意的是 Bitmap 類裏面提供的 createBitmap() 方法:
這個函數返回的 Bitmap 有可能和 source bitmap 是同一個,在回收的時候,須要特別檢查 source bitmap 與 return bitmap 的引用是否相同,只有在不等的狀況下,纔可以執行 source bitmap 的 recycle() 方法。
在 Android 程序裏面存在不少須要 register 與 unregister 的監聽器,咱們須要確保在合適的時候及時 unregister 那些監聽器。本身手動 add 的 listener,須要記得及時 remove 這個 listener。
Android 設備根據硬件與軟件的設置差別而存在不一樣大小的內存空間,他們爲應用程序設置了不一樣大小的 Heap 限制閾值。你能夠經過調用 getMemoryClass()
來獲取應用的可用 Heap 大小。在一些特殊的情景下,你能夠經過在 manifest 的 application 標籤下添加 largeHeap = true
的屬性來爲應用聲明一個更大的 heap 空間。而後,你能夠經過 getLargeMemoryClass()
來獲取到這個更大的 heap size 閾值。然而,聲明獲得更大 Heap 閾值的本意是爲了一小部分會消耗大量 RAM 的應用 ( 例如一個大圖片的編輯應用 ) 。不要輕易的由於你須要使用更多的內存而去請求一個大的 Heap Size。只有當你清楚的知道哪裏會使用大量的內存而且知道爲何這些內存必須被保留時纔去使用 large heap。所以請謹慎使用 large heap 屬性。使用額外的內存空間會影響系統總體的用戶體驗,而且會使得每次 gc 的運行時間更長。在任務切換時,系統的性能會大打折扣。另外, large heap 並不必定可以獲取到更大的 heap。在某些有嚴格限制的機器上,large heap 的大小和一般的 heap size 是同樣的。所以即便你申請了 large heap,你仍是應該經過執行 getMemoryClass()
來檢查實際獲取到的 heap 大小。
咱們知道 hdpi / xhdpi / xxhdpi 等等不一樣 dpi 的文件夾下的圖片在不一樣的設備上會通過 scale 的處理。例如咱們只在 hdpi 的目錄下放置了一張 100 x 100 的圖片,那麼根據換算關係,xxhdpi 的手機去引用那張圖片就會被拉伸到 200 x 200。須要注意到在這種狀況下,內存佔用是會顯著提升的。對於不但願被拉伸的圖片,須要放到 assets 或者 nodpi 的目錄下。
在某些狀況下,咱們須要事先評估那些可能發生 OOM 的代碼,對於這些可能發生 OOM 的代碼,加入 catch 機制,能夠考慮在 catch 裏面嘗試一次降級的內存分配操做。例如 decode bitmap 的時候,catch 到 OOM,能夠嘗試把採樣比例再增長一倍以後,再次嘗試 decode。
由於 static 的生命週期過長,和應用的進程保持一致,使用不當極可能致使對象泄漏,在 Android 中應該謹慎使用 static 對象。
雖然單例模式簡單實用,提供了不少便利性,可是由於單例的生命週期和應用保持一致,使用不合理很容易出現持有對象的泄漏。特別是持有 Context 的引用,須要謹慎對待。
越扁平化的視圖佈局,佔用的內存就越少,效率越高。咱們須要儘可能保證佈局足夠扁平化,當使用系統提供的 View 沒法實現足夠扁平的時候考慮使用自定義 View 來達到目的。
使用多進程能夠把應用中的部分組件運行在單獨的進程當中,這樣能夠擴大應用的內存佔用範圍,可是這個技術必須謹慎使用,絕大多數應用都不該該貿然使用多進程,一方面是由於使用多進程會使得代碼邏輯更加複雜,另外若是使用不當,它可能反而會致使顯著增長內存。當你的應用須要運行一個常駐後臺的任務,並且這個任務並不輕量,能夠考慮使用這個技術。
一個典型的例子是建立一個能夠長時間後臺播放的 Music Player。若是整個應用都運行在一個進程中,當後臺播放的時候,前臺的那些 UI 資源也沒有辦法獲得釋放。相似這樣的應用能夠切分紅 2 個進程:一個用來操做 UI,另一個給後臺的 Service。
內存優化並不就是說程序佔用的內存越少就越好,若是由於想要保持更低的內存佔用,而頻繁觸發執行 gc 操做,在某種程度上反而會致使應用性能總體有所降低,這裏須要綜合考慮作必定的權衡。
若是想第一時間收到更新信息的能夠關注個人簡書:簡書地址
你也能夠選擇關注個人公衆號:nanchen