1. 程式人生 > >Android效能優化解決方案

Android效能優化解決方案

Android開發做久了,你會發現很多APP出現記憶體洩漏,卡頓,載入遲緩等問題,這時你會細細體味出這樣的APP其實很一般,這說明我們也走向深度開發中,技術實力也提高了很大一部分,以下是我常見的一些記憶體洩漏問題以及優化方案.

一,記憶體洩漏

記憶體洩漏是指由於程式碼編寫不當導致不再使用的物件無法得到及時釋放。記憶體洩漏產生的記憶體垃圾不僅浪費資源,拖慢執行效率,甚至還可能造成記憶體溢位,直接導致應用崩潰。

對於Android應用,比較容易發生洩漏的是Activity、Fragment物件,此類物件的共性是其都有一定的生命週期。以Activity為例,一個Activity例項的生命起始於onCreate(),終結於onDestroy()。當一個Activity不再使用時,系統會呼叫回撥方法Activity.onDestroy()方法做一些清理操作。但是對於Activity物件本身所佔記憶體,則完全由虛擬機器的垃圾回收器來完成回收。垃圾回收器會檢查該例項是否被持有強引用,如果存在指向該物件的強引用,則不會回收其所佔記憶體空間,這塊記憶體空間也就成了記憶體垃圾。由此可見記憶體洩漏是由不當的強引用導致的。

1.1 物件的引用鏈

從GC ROOT到洩漏物件的引用鏈能精準地定位導致記憶體洩漏的原因。物件無法被垃圾回收器回收,一定是由於GC ROOT直接或間接持有了它的強引用。

常見的GCROOT有:宣告為static的變數,未停止的執行緒,Application物件,甚至是棧記憶體中的區域性變數。

**1.2 Android中常見的記憶體洩露
a.集合中物件沒清理造成的記憶體洩露**

程式設計過程中,我們常常會把一些物件加入到集合中。在我們不再需要該物件時,如果沒有及時把它從集合中清理掉,就會導致這個集合佔用的記憶體越來越大。同時如果這個集合是靜態的話,那情況就更嚴重了。如下的程式碼段中在每次啟動Activity的時候都往靜態集合中添加了一個物件,如果Activity被頻繁啟動,set將不斷變大,影響APP的正常執行。

這裡寫圖片描述

所以,集合中不再使用的物件應及時釋放掉。上述程式碼應該在Activity的onDestroy()方法中,及時清理set裡的元素,避免無用物件繼續存在強引用,例如:

這裡寫圖片描述

這樣可以保證set持有的強引用都被釋放。

單例的靜態特性使得其生命週期可能跟應用的生命週期一樣長,如果使用不恰當的話,很容易造成記憶體洩漏。

如下程式碼是一個簡單的單例模式實現:

b. 單例模式造成的記憶體洩漏

在建立單例的時候,如果我們傳入當前Activity的Context,例如:

這裡寫圖片描述

單例testContextHelper裡面一直儲存著該Activity的引用,當這個Context 對應的 Activity 退出時,由於該 Context 的引用一直被單例物件持有,所以該Activity佔用的記憶體並不會被回收,造成洩漏。在使用單例模式時,一定要避免持有短生命週期物件的引用,比如上述程式碼在引用Context時可以使用Application的Context代替Activity的Context,即:

這裡寫圖片描述

因為Application在應用的執行過程中一直存在,不會退出。

c. 非靜態內部類建立靜態例項造成的記憶體洩漏

在啟動頻繁的Activity中,為了避免反覆建立某些資源,提高載入速度,我們可能會在Activity內部建立一個靜態例項,每次啟動Activity時都會使用該例項,如下程式碼:

這裡寫圖片描述

此時Activity內部有一個靜態單例,且為非靜態內部類的例項。由於非靜態內部類預設會持有外部類的引用,並且該類建立了一個靜態例項,該例項的生命週期和應用的一樣長,這就導致了該靜態例項一直會持有該Activity的引用,導致Activity的記憶體資源不能正常回收。為了避免這一問題,在使用過程中,正確的做法是將內部類設為靜態類或者變成單獨的類。

d. 使用handler時的記憶體問題

在Android應用中,Handler通過傳送Message與其他執行緒互動,發出的Message被儲存在目標執行緒的MessageQueue中的,並且Message不一定馬上就被處理,駐留時間可能比較久。比如我們用Handler傳送一個延時比較久的Message:

這裡寫圖片描述

而Message中持有Handler例項的強引用,如果Message在Queue中一直存在,就會導致Handler例項無法被回收,而Handler持有Activity的強引用,Activity物件也不會被回收,這就造成了例項洩露。所以,在建立Handler時,最好使用弱引用來引用目標Activity物件,比如:

這裡寫圖片描述

這樣可以避免由於Handler持有強引用導致Activity無法回收。
e. 靜態成員變數造成的記憶體洩露

如果成員變數被宣告為 static,其生命週期將與整個應用程序的生命週期一樣。如果靜態變數直接或間接強引用了某一短生命週期物件(比如Activity),這會導致即使app切到後臺,這部分記憶體也不會被釋放。下面的錯誤示範程式碼中,在Activity啟動的時候,直接將其引用賦給了靜態變數obj,會導致該Activity一直不能被回收,導致記憶體洩露。

因此,在使用靜態變數時,應該避免其持有短生命週期物件的強引用,可以使用弱引用來代替強引用。

f. 資源未關閉造成的記憶體洩漏

對於使用了BroadcastReceiver,ContentObserver,File,遊標Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者登出,否則這些資源將可能不會被回收,造成記憶體洩漏。雖然有些系統程式,它本身可以自動取消註冊的(非即時),但是我們還是應該在我們的程式中明確的取消註冊,程式結束時應該把所有的註冊都取消掉。

以上就是常見的效能優化方法,至於最簡單的佈局優化,我就不一一介紹了,這些都要你自己去解決,當然了還需要會利用常見的優化工具,
列如:
MQC http://mqc.yunos.com/alert.htm
MAT等等,只要會使用一種工具就可以分析問題了,再次如有什麼問題,我們一起交流,解決!

這裡寫圖片描述