如何在Android中避免建立不必要的物件
在程式設計開發中,記憶體的佔用是我們經常要面對的現實,通常的記憶體調優的方向就是儘量減少記憶體的佔用。這其中避免建立不必要的物件是一項重要的方面。
Android裝置不像PC那樣有著足夠大的記憶體,而且單個App佔用的記憶體實際上是比較小的。所以避免建立不必要的物件對於Android開發尤為重要。
本文會介紹一些常見的避免建立物件的場景和方法,其中有些屬於微優化,有的屬於編碼技巧,當然也有確實能夠起到顯著效果的方法。
使用單例
單例是我們常用的設計模式,使用這種模式,我們可以只提供一個物件供全域性呼叫。因此單例是避免建立不必要的物件的一種方式。
單例模式上手容易,但是需要注意很多問題,最重要的就是多執行緒併發的情況下保證單例的唯一性。當然方式很多,比如餓漢式,懶漢式double-check等。這裡介紹一個很極客的書寫單例的方式。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
在Java中,類的靜態初始化會在類被載入時觸發,我們利用這個原理,可以實現利用這一特性,結合內部類,可以實現上面的程式碼,進行懶漢式建立例項。
關於單例,可以詳細參考文章單例這種設計模式
避免進行隱式裝箱
自動裝箱是Java 5 引入的一個特性,即自動將原始型別的資料轉換成對應的引用型別,比如將int轉為Integer等。
這種特性,極大的減少了編碼時的瑣碎工作,但是稍有不注意就可能建立了不必要的物件了。比如下面的程式碼
1 2 3 4 |
|
上面的程式碼sum+=i可以看成sum = sum + i,但是+這個操作符不適用於Integer物件,首先sum進行自動拆箱操作,進行數值相加操作,最後發生自動裝箱操作轉換成Integer物件。其內部變化如下
1 2 |
|
由於我們這裡宣告的sum為Integer型別,在上面的迴圈中會建立將近4000個無用的Integer物件,在這樣龐大的迴圈中,會降低程式的效能並且加重了垃圾回收的工作量。因此在我們程式設計時,需要注意到這一點,正確地宣告變數型別,避免因為自動裝箱引起的效能問題。
另外,當將原始資料型別的值加入集合中時,也會發生自動裝箱,所以這個過程中也是有物件建立的。如有需要避免這種情況,可以選擇SparseArray
,SparseBooleanArray
,SparseLongArray
等容器。
關於Java中的自動裝箱與拆箱,參考文章Java中的自動裝箱與拆箱
謹慎選用容器
Java和Android提供了很多編輯的容器集合來組織物件。比如ArrayList
,ContentValues
,HashMap
等。
然而,這樣容器雖然使用起來方便,但也存在一些問題,就是他們會自動擴容,這其中不是建立新的物件,而是建立一個更大的容器物件。這就意味這將佔用更大的記憶體空間。
以HashMap為例,當我們put key和value時,會檢測是否需要擴容,如需要則雙倍擴容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
關於擴容的問題,通常有如下幾種方法
- 預估一個較大的容量值,避免多次擴容
- 尋找替代的資料結構,確保做到時間和空間的平衡
用好LaunchMode
提到LaunchMode必然和Activity有關係。正常情況下我們在manifest中宣告Activity,如果不設定LaunchMode就使用預設的standard模式。
一旦設定成standard,每當有一次Intent請求,就會建立一個新的Activity例項。舉個例子,如果有10個撰寫郵件的Intent,那麼就會建立10個ComposeMailActivity的例項來處理這些Intent。結果很明顯,這種模式會建立某個Activity的多個例項。
如果對於一個搜尋功能的Activity,實際上保持一個Activity示例就可以了,使用standard模式會造成Activity例項的過多建立,因而不好。