Android設備作為一種移動設備,無論是內存還是CPU的性能都受到了很大的限制,這導致Android程序的性能問題異常突出,對於性能優化提出了更高的要求。本篇文章根據Android開發中一些有效的性能優化方法,貼出一些關於性能優化方面的技術文章,為Android開發中有關性能優化方面的學習提供一個參考。

一、Android性能優化的方面

參考:《Android開發藝術探索》

針對Android的性能優化,主要有以下幾個有效的優化方法:

1.佈局優化

2.繪製優化

3.內存泄漏優化

4.響應速度優化

5.ListView/RecycleView及Bitmap優化

6.線程優化

7.其他性能優化的建議

下面我們具體來介紹關於以上這幾個方面優化的具體思路及解決方案。

二、佈局優化

關於佈局優化的思想很簡單,就是盡量減少佈局文件的層級。這個道理很淺顯,佈局中的層級少了,就意味著Android繪製時的工作量少了,那麼程序的性能自然就提高了。

如何進行佈局優化?

①刪除佈局中無用的控制項和層次,其次有選擇地使用性能比較低的ViewGroup。

關於有選擇地使用性能比較低的ViewGroup,這就需要我們開發就實際靈活選擇了。

例如:如果佈局中既可以使用LinearLayout也可以使用RelativeLayout,那麼就採用LinearLayout,這是因為RelativeLayout的功能比較複雜,它的佈局過程需要花費更多的CPU時間。FrameLayout和LinearLayout一樣都是一種簡單高效的ViewGroup,因此可以考慮使用它們,但是很多時候單純通過一個LinearLayout或者FrameLayout無法實現產品效果,需要通過嵌套的方式來完成。這種情況下還是建議採用RelativeLayout,因為ViewGroup的嵌套就相當於增加了佈局的層級,同樣會降低程序的性能。

②採用<include>標籤,<merge>標籤,ViewStub。

<include>標籤主要用於佈局重用。

<merge>標籤一般和<include>配合使用,可以降低減少佈局的層級。

ViewStub提供了按需載入的功能,當需要時才會將ViewStub中的佈局載入到內存,提高了程序初始化效率。

關於它們的使用方法,參考:Android佈局優化之include、merge、ViewStub的使用

③避免多度繪製

過度繪製(Overdraw)描述的是屏幕上的某個像素在同一幀的時間內被繪製了多次。在多層次重疊的 UI 結構裡面,如果不可見的 UI 也在做繪製的操作,會導致某些像素區域被繪製了多次,同時也會浪費大量的 CPU 以及 GPU 資源。

如下所示,有些部分在佈局時,會被重複繪製。

關於過度繪製產生的一般場景及解決方案,參考:Android 過度繪製優化

三、繪製優化

繪製優化是指View的onDraw方法要避免執行大量的操作,這主要體現在兩個方面:

①onDraw中不要創建新的局部對象。

因為onDraw方法可能會被頻繁調用,這樣就會在一瞬間產生大量的臨時對象,這不僅佔用了過多的內存而且還會導致系統更加頻繁gc,降低了程序的執行效率。

②onDraw方法中不要做耗時的任務,也不能執行成千上萬次的循環操作,儘管每次循環都很輕量級,但是大量的循環仍然十分搶佔CPU的時間片,這會造成View的繪製過程不流暢。

按照Google官方給出的性能優化典範中的標準,View的繪製頻率保證60fps是最佳的,這就要求每幀繪製時間不超過16ms(16ms = 1000/60),雖然程序很難保證16ms這個時間,但是盡量降低onDraw方法中的複雜度總是切實有效的。

四、內存泄漏優化

內存泄漏是開發過程中的一個需要重視的問題,但是由於內存泄露問題對開發人員的經驗和開發意識有較高的要求,因此也是開發人員最容易犯的錯誤之一。

內存泄露的優化分為兩個方面:

①在開發過程中避免寫出有內存泄漏的代碼

②通過一些分析工具比如MAT來找出潛在的內存泄露,然後解決。

對應於兩種不同情況,一個是瞭解內存泄漏的可能場景以及如何規避,二是怎麼查找內存泄漏。

1.那麼我們就先了解什麼是內存泄漏?這樣我們才能知道如何避免。

大家都知道,java是有垃圾回收機制的,這使得java程序員比C++程序員輕鬆了許多,存儲申請了,不用心心念念要加一句釋放,java虛擬機會派出一些回收線程兢兢業業不定時地回收那些不再被需要的內存空間(注意回收的不是對象本身,而是對象佔據的內存空間)。

Q1:什麼叫不再被需要的內存空間?

答:Java沒有指針,全憑引用來和對象進行關聯,通過引用來操作對象。如果一個對象沒有與任何引用關聯,那麼這個對象也就不太可能被使用到了,回收器便是把這些「無任何引用的對象」作為目標,回收了它們佔據的內存空間。

Q2:如何分辨為對象無引用?

答:2種方法

引用計數法直接計數,簡單高效,Python便是採用該方法。但是如果出現 兩個對象相互引用,即使它們都無法被外界訪問到,計數器不為0它們也始終不會被回收。為瞭解決該問題,java採用的是b方法。

可達性分析法這個方法設置了一系列的「GC Roots」對象作為索引起點,如果一個對象 與起點對象之間均無可達路徑,那麼這個不可達的對象就會成為回收對象。這種方法處理 兩個對象相互引用的問題,如果兩個對象均沒有外部引用,會被判斷為不可達對象進而被回收(如下圖)。

[圖片上傳失敗...(image-56f3fc-1558960935591)]

Q3:有了回收機制,放心大膽用不會有內存泄漏?

答:答案當然是No!

雖然垃圾回收器會幫我們幹掉大部分無用的內存空間,但是對於還保持著引用,但邏輯上已經不會再用到的對象,垃圾回收器不會回收它們。這些對象積累在內存中,直到程序結束,就是我們所說的「內存泄漏」。

當然了,用戶對單次的內存泄漏並沒有什麼感知,但當泄漏積累到內存都被消耗完,就會導致卡頓,崩潰。

下面這張圖可以幫助我們更好地理解對象的狀態,以及內存泄漏的情況

左邊未引用的對象是會被GC回收的,右邊被引用的對象不會被GC回收,但是未使用的對象中除了未引用的對象,還包括已被引用的一部分對象,那麼內存泄漏久發生這部分已被引用但未使用的對象。

2.Android一般在什麼情況下會出現內存泄漏?

①集合類泄漏

②單例/靜態變數造成的內存泄漏③匿名內部類/非靜態內部類④資源未關閉造成的內存泄漏

大概可以分為以上幾類,還有一些經常會聽到的Hanlder,AsyncTask引起內存泄漏,都屬於上述③中的情況。

那麼上述四種情況是怎麼造成的內存泄漏,具體是什麼原因,以及Android中一些知名的引起內存泄漏的原因,以及解決方法是怎麼樣的?

關於這部分內容,看了一些較好的文章,向大家推薦一下:

[譯]Android內存泄漏的八種可能(上)

[譯]Android防止內存泄漏的八種方法(下)Android 內存泄漏總結

3.Android怎麼分析內存泄漏?

上面介紹了內存泄漏的場景,對應的有一些解決方案。

那麼在內存泄漏已經發生的情況下,我們該如何解決呢?

我們可以通過MAT(Memory Analyzer Tool),或者 LeakCanary來檢測Android中的內存泄漏。

上面的文章:Android 內存泄漏總結 也介紹瞭如何使用MAT,或者 LeakCanary來檢測Android中的內存泄漏。

這篇文章Android 性能優化之使用MAT分析內存泄露問題詳細介紹了使用MAT來檢測Android中的內存泄漏。

五、響應速度優化

響應速度優化的核心思想就是避免在主線程中做耗時操作

如果有耗時操作,可以開啟子線程執行,即採用非同步的方式來執行耗時操作。

如果在主線程中做太多事情,會導致Activity啟動時出現黑屏現象,甚至ANR。

Android規定,Activity如果5秒鐘之內無法響應屏幕觸摸事件或者鍵盤輸入事件就會出現ANR,而BroadcastReceiver如果10秒鐘之內還未執行完操作也會出現ANR。

為了避免ANR,可以開啟子線程執行耗時操作,但是子線程不能更新UI,所以需要子線程與主線程進行通信來解決子線程執行耗時任務後,通知主線程更新UI的場景。關於這部分,需要掌握Handler消息機制,AsyncTask,IntentService等內容。

然而,在實際開發中,ANR仍然不可避免的發生了,而且很難從代碼上發現,這時候就要用到ANR日誌分析。當一個進程發生了ANR之後,系統會在/data/anr目錄下創建一個文件traces.txt,通過分析這個文件就能定位出ANR的原因。

六、ListView/RecycleView及Bitmap優化

ListView/RecycleView的優化思想主要從以下幾個方面入手:

①使用ViewHolder模式來提高效率

②非同步載入:耗時的操作放在非同步線程中

③ListView/RecycleView的滑動時停止載入和分頁載入

具體優化建議及詳情,參考:ListView的優化

Bitmap優化

主要是對載入圖片進行壓縮,避免載入圖片多大導致OOM出現。

具體圖片壓縮方案,參考:徹底理解Bitmap的高效載入策略

七、線程優化

線程優化的思想就是採用線程池,避免程序中存在大量的Thread。線程池可以重用內部的線程,從而避免了線程的創建和銷毀鎖帶來的性能開銷,同時線程池還能有效地控制線程池的最大並法術,避免大量的線程因互相搶佔系統資源從而導致阻塞現象的發生。因此在實際開發中,盡量採用線程池,而不是每次都要創建一個Thread對象。

關於線程池詳解及使用,參考:Java線程池詳解。

八、其他性能優化建議

①避免過度的創建對象

②不要過度使用枚舉,枚舉佔用的內存空間要比整型大

③常量請使用static final來修飾

④使用一些Android特有的數據結構,比如SparseArray和Pair等

⑤適當採用軟引用和弱引用

⑥採用內存緩存和磁碟緩存

⑦盡量採用靜態內部類,這樣可以避免潛在的由於內部類而導致的內存泄漏。

以上是關於Android性能優化方面,我們一些入手點。從這些方面,我們可以在平時的開發中注意,避免類似錯誤,提高Android程序的性能,但是其中一些方面的要求則需要我們不斷的學習,以及平時良好的意識與習慣。由於自己開發經驗幾乎為0,沒辦法根據實際經驗來說明,只能寫下這篇文章來提醒自己以後開發的時候需要注意和培養的地方。

自己是從事了七年開發的Android工程師,不少人私下問我,2019年Android進階該怎麼學,方法有沒有?

沒錯,年初我花了一個多月的時間整理出來的學習資料,希望能幫助那些想進階提升Android開發,卻又不知道怎麼進階學習的朋友。【包括高級UI、性能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料】,希望能幫助到您面試前的複習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。

喜歡我的文章可以點贊+關注我的【個人主頁】獲取免費資料,後續我將繼續分享更多Android技術乾貨,感謝支持!

資料大全

推薦閱讀:
相關文章