當前位置:
首頁 > 科技 > Android性能優化全方面解析

Android性能優化全方面解析

目的


公司的新需求終於解決完了,離測試和發布還有段時間,第一次體驗了下沒需求沒bug的感覺,真是舒爽,然後翻了翻有什麼可以學的。無意翻到了Android後期發展的五大趨勢。一、性能優化。二、高級UI。三、JNI/NDK開發。四、架構師。五、RN開發。這也許將會是我的進階趨勢。早已知道在瓶頸期的我,似乎看到了突破的希望的。初級進階中級也好,中級進階高級也罷,現在的市場無非是根據經驗規定的,根據能力的少之又少。


其實,關注我的或者在群里的小夥伴也知道,UI那塊我問題不大。但是高級UI就有難度了。我們先不管他,一個一個來。先從性能優化來。其實我是拒絕寫這篇文章的。為什麼?性能優化的分類很多,一個分類寫一篇感覺篇幅量很小,結合在一起寫有感覺很大。而我目前打算整體的整理一下。


那麼我們先分析下性能優化有那幾個方面:一、內存優化。二、UI優化(布局優化和繪製優化)。三、速度的優化(線程優化/網路優化)。四、電量優化。五、啟動優化。應該就這些了。那麼這只是五大方面,裡面還結合了各種細節方面的。不急,我們下面一個個地介紹。


內存優化

關於性能優化我們可以不知道其他的,但一定要知道內存優化。因為內存泄漏可以Android的常客。那麼什麼是內存泄漏呢?內存不在GC的掌控範圍之內了。那麼Java的GC內存回收機制是什麼?某對象不在有任何引用的時候才會進行回收。那麼GC回收機制的原理是什麼?又或者說可以作為GCRoot引用點的是啥?或許有人聽不懂我在講啥。我們先來看張圖。

Android性能優化全方面解析



當我們向上尋找,一直尋找到GC Root的時候,此對象不會進行回收,例如,一個Activity。那麼如果我們向上尋找,直到找到GC Root對象的時候,就說明它是不可以回收的,例如,我定義了一個int a;但是這個數據,我整個頁面或者說整個項目都沒有用到,則這個對象會被GC掉。


GC的引用點


1. Java棧中引用的對象


2. 方法靜態引用的對象


3. 方法常量引用的對象


4. Native中JNI引用的對象


5. Thread——「活著的」線程

如何判斷


那麼我們如何判斷一個對象是一個垃圾對象,可以講他進行回收呢?舉了小例子教你們如何區分:


一般在學校吃飯,我們有兩種情況,第一:吃完飯就直接走人,碗筷留給阿姨來收拾處理。


第二:吃完之後把碗筷放到收盤處直接進行回收。


但我們是個有素質的人,一般採用第二種情況,但根據想法,我們更傾向於第一種。


那麼一般在飯店或者KFC中,都是第一種情況。


那麼此時,問題來了,如果我已經吃完飯,然後我並沒有離開飯店,做在位置上和朋友吹吹牛逼,談談理想,聊聊人生。


那麼桌上那一堆碗筷是收還是不收?講道理是不能收的。雖然實際也是不能收的。因為顧客是上帝~~~


So,我們如何判斷一個對象是一個可回收的垃圾對象呢?這是我們的一個主觀的判斷。但是有種情況我們是必須要考慮到的,沒錯,就是內存過多無法釋放的時候,會直接導致OOM。整個項目boom炸了。什麼鬼?outofmemory。沒錯就是它。


內存溢出

分析原因


我們需要分析內存溢出的原因,我們先來看一張圖:

Android性能優化全方面解析



內存泄漏一般導致應用卡頓,極端情況會導致項目boom。Boom的原因是因為超過內存的閾值。


原因主要有兩方面:


代碼存在泄漏,內存無法及時釋放導致oom(這個我們後面說)


一些邏輯消耗了大量內存,無法及時釋放或者超過導致oom


所謂消耗大量的內存的,絕大多數是因為圖片載入。這是我們oom出現最頻繁的地方。我前面有寫過圖片載入的方法,一個是控制每次載入的數量,第二,保證每次滑動的時候不進行載入,滑動完進行載入。一般情況使用先進後出,而不是先進先出。不過一般我們圖片載入都是使用fresco或者Glide等開源庫。


我們來看下下面兩張圖:

Android性能優化全方面解析


Android性能優化全方面解析



對比兩張圖,我們可以在第一張的情況出現了oom情況,我們通過log列印發現,處理的好像沒什麼問題,換句話說,如果我不放那0.8M的圖片。然後繼續不停的操作同樣會出現OOM,然而我們就蒙了。沒什麼圖片載入怎麼就這麼崩掉了。


如何查看


首先,我們確定我們項目或者某幾個類裡面是否存在內存溢出的問題。我們可以通過如下方法:


Android–>System Information–>MemoryUsage查看Object裡面是否有沒有被釋放的Views和Activity


命令行模式:adb shell dumpsys meminfo 包名 -d


就那我公司的項目舉例把。首先,我們在這邊可以看到memory。CPU和net的使用情況。

Android性能優化全方面解析



我們找到Object。看看我們內存的消耗情況。

Android性能優化全方面解析



隨便這麼一看,1300左右的view和一個Activity,還有3個context——可以理解為一個Activity裡面使用了將近1300個view,想都不敢想。


我們可以通過看Memory Monitor工具,檢查一個一個的動作(比如Activity的跳轉)。反覆多次執行某一個操作,不斷通過這個工具查看內存的大概變化情況。 前後兩個內存變化增加了不少。


我們可以更仔細查找泄漏的位置,在AS裡面使用 Heap SnapShot工具(堆棧快照)。如圖所示:

Android性能優化全方面解析


我們點擊後,他會進行一段時間的監控,然後會生成一個文件。我們點擊我們package tree view。

Android性能優化全方面解析



我們找到自己項目的包名。然後進行進一步的分析。首先看一下2個列表的列名到底指的什麼。

Android性能優化全方面解析



實例化對象的詳細信息:

Android性能優化全方面解析



我們來簡單看一下內存中的數量:

這還是我們剛進手機,一個bean就被調用了這麼多次。簡直可怕。這個我們可以通過內存分析工具解決的。


內存分析工具


性能優化工具:


Heap SnapShot工具


Heap Viewer工具


LeakCanary工具


MAT工具


TraceView工具(Device Monitor)


第三方分析工具:


MemoryAnalyzer

GT Home


iTest


因為我沒有這些工具,無法進行演示。


注意事項


我們盡量不要使用Activity的上下文,而是使用application的上下文,因為application的生命周期長,進程退出時才會被銷毀。所以,單例模式是最容易造成內存溢出的原本所在,因為單例模式的生命周期的應該和application的生命周期一樣長,而不是和Activity的相同。


Animation也會導致內存溢出,為什麼?因為我們是通過view來進行演示的,導致view被Activity持有,而Activity又持有view。最後因為Activity無法釋放,導致內存泄漏。解決方法是在Activity的ondestory()方法中調用Animation.cancle()進行停止,當然一些簡單的動畫我們可以通過自定義view來解決。至少我現在已經很少使用Animation了。沒有一個動畫是自定義view解決不了的。如果有,那就是兩個。


UI優化


UI優化主要包括布局優化以及view的繪製優化。不急,我們接下來一個一個慢慢看~~。先說下UI的優化到底是什麼?有些時候我們打開某個軟體,會出現卡頓的情況。這就是UI的問題。那麼我們想一下,什麼情況會導致卡頓呢?一般是如下幾種情況:


1. 人為在UI線程中做輕微耗時操作,導致UI線程卡頓;


2. 布局Layout過於複雜,無法在16ms內完成渲染;

3. 同一時間動畫執行的次數過多,導致CPU或GPU負載過重;


4. View過度繪製,導致某些像素在同一幀時間內被繪製多次,從而使CPU或GPU負載過重;


5. View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;


6. 內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;


7. 冗餘資源及邏輯等導致載入和執行緩慢;


8. 臭名昭著的ANR。


可以看見,上面這些導致卡頓的原因都是我們平時開發中非常常見的。有些人可能會覺得自己的應用用著還蠻OK的,其實那是因為你沒進行一些瞬時測試和壓力測試,一旦在這種環境下運行你的App你就會發現很多性能問題。


布局優化


GPU繪製


我們對於UI性能的優化還可以通過開發者選項中的GPU過度繪製工具來進行分析。在設置->開發者選項->調試GPU過度繪製(不同設備可能位置或者叫法不同)中打開調試後可以看見如下圖(對settings當前界面過度繪製進行分析):

Android性能優化全方面解析


Android性能優化全方面解析



這圖看著太亂,我們來一張簡潔明了的圖:

Android性能優化全方面解析



我們的目標就是盡量減少紅色Overdraw,看到更多的藍色區域。


可以發現,開啟後在我們想要調試的應用界面中可以看到各種顏色的區域,具體含義如下:

Android性能優化全方面解析



Overdraw有時候是因為你的UI布局存在大量重疊的部分,還有的時候是因為非必須的重疊背景。例如某個Activity有一個背景,然后里面的Layout又有自己的背景,同時子View又分別有自己的背景。僅僅是通過移除非必須的背景圖片,這就能夠減少大量的紅色Overdraw區域,增加藍色區域的佔比。這一措施能夠顯著提升程序性能。


如果布局中既能採用RealtiveLayout和LinearLayout,那麼直接使用LinearLayout,因為Relativelayout的布局比較複雜,繪製的時候需要花費更多的CPU時間。如果需要多個LinearLayout或者Framelayout嵌套,那麼可採用Relativelayout。因為多層嵌套導致布局的繪製有大部分是重複的,這會減少程序的性能。


GPU呈現模式分析


我們依舊打開設置–>開發者選項–>GPU呈現模式分析–>在屏幕上顯示為條形圖,如圖所示:

Android性能優化全方面解析


Android性能優化全方面解析



當然,也可以在執行完UI滑動操作後在命令行輸入如下命令查看命令行列印的GPU渲染數據(分析依據:Draw + Process + Execute = 完整的顯示一幀時間 < 16ms):


隨著界面的刷新,界面上會以實時柱狀圖來顯示每幀的渲染時間,柱狀圖越高表示渲染時間越長,每個柱狀圖偏上都有一根代表16ms基準的綠色橫線,每一條豎著的柱狀線都包含三部分(藍色代表測量繪製Display List的時間,紅色代表OpenGL渲染Display List所需要的時間,黃色代表CPU等待GPU處理的時間),只要我們每一幀的總時間低於基準線就不會發生UI卡頓問題(個別超出基準線其實也不算啥問題的)。就簡單的看下我們公司項目剛啟動的時候:

Android性能優化全方面解析



突然就有那麼一種想吐槽的感覺,我記得之前我做了瘦身的優化,但是要讓我做性能優化,我覺得應該沒那麼簡單。


代碼優化


Android Studio和IntellJ idead都有自帶的代碼檢查工具。打開Analyze->Run Inspection by Name… –>unused resource 點擊開始檢測,等待一下後會發現如下結果:

Android性能優化全方面解析



我們還可以這樣,將滑鼠放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->點擊確認開始檢測,等待一下後會發現如下結果:

Android性能優化全方面解析



當然,我這只是截取了少一部分,我們看下下面那個提示:@param v tag description is missing 。意味著v的類型缺少了,要麼補上介紹,要麼直接刪除。


上面那兩種方法是最容易找到代碼缺陷以及無用代碼的地方。所以盡情的入坑去填坑把~~~


繪製優化


那麼什麼是繪製優化?繪製優化主要是指View的Ondraw方法需要避免執行大量的操作。我將分為了2個方面。


ondraw方法不需要創建新的局部對象,這是因為ondraw方法是實時執行的,這樣會產品大量的臨時對象,導致佔用了更多內存,並且使系統不斷的GC。降低了執行效率。


Ondraw方法不需要執行耗時操作,在ondraw方法里少使用循環,因為循環會佔用CPU的時間。導致繪製不流暢,卡頓等等。Google官方指出,view的繪製幀率穩定在60dps,這要求每幀的繪製時間不超過16ms(1000/60)。雖然很難保證,但我們需要儘可能的降低。


60dps是目前最合適的圖像顯示速度,也是絕大部分Android設備設置的調試頻率,如果在16ms內順利完成界面刷新操作可以展示出流暢的畫面,而由於任何原因導致接收到VSYNC信號的時候無法完成本次刷新操作,就會產生掉幀的現象,刷新幀率自然也就跟著下降(假定刷新幀率由正常的60fps降到30fps,用戶就會明顯感知到卡頓)。So,前面我們說GPU的時候也談到了這個。總的而言,感覺還是蠻重要的。


網路優化


線程是我們項目中不可缺少的重要部分,因為我們大多數數據都是從網路獲取的。So,線程這個是必備用品。


我們依舊可以通過Memory下面的Net進行網路的監聽:


ANR問題


相信這個問題在座的各種沒少遇到過,那麼什麼是ANR?application not responding。應用程序無響應。那麼一般什麼時候會出現ANR。Android官方規定:activity如果5s內無響應事件(屏幕觸摸事件或者鍵盤輸入事件)。BroadcastReceiver如果在10s內無法處理完成。Service如果20s內無法處理完成。這三種情況會導致ANR。用張簡潔的圖來介紹把。看起來方便。

Android性能優化全方面解析



線程優化


上面說的三種導致ANR的情況,絕大多數就是因為線程阻塞導致的。那麼我們應該如何處理呢?Android系統為我們提供了若干組工具類來解決此問題。


Asynctask:為UI線程與工作線程之間進行快速處理的切換提供一種簡單便捷的機制。適用於當下立即需要啟動,但是非同步執行的生命周期短暫的場景。


HandlerThread:為某些回調方法或者等待某些執行任務的執行設置一個專屬的線程,並提供線程任務的調度機制。


ThreadPool:把任務分解成不同的單元,分發到各個不同的線程上,進行同時並發處理。


IntentService:適合執行由Ui觸發的後台任務。並可以把這些任務執行的情況通過一定的機制反饋給UI。


網路請求耗時會給用戶帶來卡頓的產品體驗,雖然可以使用Loading提升用戶體驗,但屬於治標不治本。例如,當網路差的時候我們公司的項目一個loading就是10多s,甚至更多。我就記得我當時面試之前下了一次我們公司的項目,因為網差的問題,一個loading一分多鐘,當時砸手機的衝動都有了,別說卸軟體了。


一般多線程的情況我們可以通過Asynctask處理。(這玩意我真沒怎麼用過- -)我前面有說過annotation。這是google官方推出的註解。比bufferknife強大很多。這個可以快捷方便的處理多線程而且不會導致線程阻塞,而且你也可以控制線程的順序,例如我要執行完線程A後,根據線程A的某個參數來執行線程B。以此類推…..


至於線程池么,最多的還是要說道圖片載入了~~。圖片載入用三方就行了~想看詳細介紹,我前面有說,當然除了這個還有下載操作。這就和IntentService有關聯了。一般下載我很少涉及到,用過幾次android原生的downloadmanager,感覺略坑。


KO網路優化


現在講網路優化的重點了,一般用到網最最最主要的是什麼?時間!!速度!!成功率!!


圖片處理


這已經不是第一次在此文提到圖片了。可見圖片的重要性。


使用WebP格式;同樣的照片,採用WebP格式可大幅節省流量,相對於JPG格式的圖片,流量能節省將近 25% 到 35 %;相對於 PNG 格式的圖片,流量可以節省將近80%。最重要的是使用WebP之後圖片質量也沒有改變。So,去和後台的小夥伴們商量吧。


使用縮略圖,我在前面寫圖片載入有說過,就是控制他的inside和option。然後進行圖片縮放。壓縮?講道理….我並不知道網路圖片怎麼壓縮,but,我會縮放啊,反正也不會失真。


網路請求處理


我們可以對服務端返回數據進行緩存,設定有效時間,有效時間之內不走網路請求,減少流量消耗。對網路的緩存可以參見HttpResponseCache。


在某些情況,我們盡量少使用GPS定位,如果條件允許,儘可能使用網路定位。


下載、上傳,我們儘可能使用斷點,說個簡單的,我在公司,準備下一個500M的遊戲,但是下到200M的時候我下班了,此時沒有了無線網,我們可以回家後用無線繼續下載。So,斷點續傳,斷點下載也是我們的必修課~,所以我前面單獨提了一篇斷點續傳的文章。


刷新數據時,儘可能使用局部刷新,而不是全局刷新,第一、界面會閃屏一下,網差的界面直接白屏一段時間也不是不可能。第二、流量的使用!!我又要拿我們公司項目搞事情了。一個閃屏的緩存60+M。。。沒錯,就是60+M。簡直可怕,我清個3、5次緩存,在打開個3、5次。好了,2分鐘時間,我一個月流量就沒了。。。So,我前面提到的網路緩存很重要,至於會不會加在項目中,我還是要看了在說- - 一個不小心,整個項目炸了都有可能。


啟動優化


眾所周知,一個好的產品,除了功能強大,好的性能也必不可少。有調查顯示,近50%的受訪者因為apk太大而拒絕使用,近40%的受訪者會因為APP性能差而卸載,性能也是造成APP用戶沮喪的頭號原因。


安卓應用的啟動方式分為三種:冷啟動、暖啟動、熱啟動,不同的啟動方式決定了應用UI對用戶可見所需要花費的時間長短。顧名思義,冷啟動消耗的時間最長。基於冷啟動方式的優化工作也是最考驗產品用戶體驗的地方。談及優化之前,我們先看看這三種啟動方式的應用場景,以及啟動過程中系統都做了些什麼工作。


冷啟動


為什麼說冷啟動是耗時最長的。冷啟動是在啟動應用前,系統沒有獲取到當前app的activity、Service等等。例如,第一次啟動app。又或者說殺死進程後第一次啟動。那麼對比其他兩種方式。冷啟動自然是耗時最久的。


應用發生冷啟動時,系統一定會執行下面的三個任務:


開始載入並啟動應用


應用啟動後,顯示一個空白的啟動窗口(啟動閃屏頁)


創建應用信息


那麼創建應用信息,系統就需要做一屁股的事:


application的初始化


啟動UI線程


創建Activity


導入視圖(inflate view)


計算視圖大小(onmesure view)


得到視圖排版(onlayout view)


繪製視圖(ondraw view)


這其中有兩個 creation 工作,分別為 Application 和 Activity creation。他們均在 View 繪製展示之前。所以,在應用自定義的 Application 類和 第一個 Activity 類中,onCreate() 方法做的事情越多,冷啟動消耗的時間越長。


暖啟動


當應用中的 Activities 被銷毀,但在內存中常駐時,應用的啟動方式就會變為暖啟動。相比冷啟動,暖啟動過程減少了對象初始化、布局載入等工作,啟動時間更短。但啟動時,系統依然會展示閃屏頁,直到第一個 Activity 的內容呈現為止。


熱啟動


相比暖啟動,熱啟動時應用做的工作更少,啟動時間更短。熱啟動產生的場景很多,常見如:用戶使用返回鍵退出應用,然後馬上又重新啟動應用。


如何優化


我們先對比下三種啟動的時間對比:


冷啟動:

Android性能優化全方面解析



暖啟動 :

Android性能優化全方面解析



熱啟動:

Android性能優化全方面解析



我們可以看到三者的明顯的差距,一個冷啟動將近一分鐘,反正我是不想看,每次跑項目都好慢~那麼我們應該怎麼做?看到有些人介紹說改變項目的theme。把它改成launcher的theme。但我覺得,這種做測試的確沒問題。但是一般項目都會有閃屏頁。然後從閃屏跳轉到首頁。我們可以按照大多數的項目來改善。怎麼說的,我們可以看到一般項目都有倒計時顯示。也就是說倒計時結束就自動進入首頁。或者可以直接跳過進入首頁。也就是說我們可以通過此方法來進行,也就是說只要他倒計時結束,不管請求是否全部獲取完我們都直接進入首頁。我們可以在閃屏頁進行一些必要的載入,例如用戶信息,定位等等,那麼至於其他的,我們可以進入主頁進行預載入。就和熱更新一樣,在用戶不知情的情況下,默默的更新bug。So,對於一些網路請求,例如廣告之類的。我們可以通過此方法進行預載入。


我們還可以這樣,閃屏頁我們把他當作一個fragment嵌套在MainActivity中,那麼我們可以在進入閃屏時直接預載入主頁的view。倒計時我們把閃屏頁remove掉直接顯示首頁。


通過上面的介紹,我們對啟動優化有了一定的了解,其實總結的話很簡單。就是減少耗時操作,總結如下:


主線程中涉及到Shareperference能否在非UI線程執行。


Application的創建過程中盡量少的進行耗時操作。


減少布局的層次,並且生命周期回調的方法中盡量減少耗時的操作。


電量優化


有了UI優化、內存優化、代碼優化、網路優化之後我們在來說說應用開發中很重要的一個優化模塊—–電量優化。


耗電概念


其實大多數開發者對電量優化的重視程度極低,其實提到性能優化想到的就是內存優化,但我們不能忽視其他的優化,電量優化其實還是必要的,例如愛奇藝、優酷等等的視頻播放器以及音樂播放器。眾所周知,音樂和視頻其實是耗電量最大的。如果用戶一旦發現我們的應用非常耗電,不好意思,他們大多會選擇卸載來解決此類問題。為此,我們需要進行優化。


如何優化


其實我們把上面那四種優化解決了,就是最好的電量優化。So,對於電量優化,我在此提一些建議:


需要進行網路請求時,我們需先判斷網路當前的狀態。


在多網路請求的情況下,最好進行批量處理,盡量避免頻繁的間隔網路請求。


在同時有wifi和移動數據的情況下,我們應該直接屏幕移動數據的網路請求,只有當wifi斷開時在調用,因為,wifi請求的耗電量遠比移動數據的耗電量低的低。


後台任務要儘可能少的喚醒CPU。(比方說,鎖屏時,QQ的消息提示行就是喚醒了CPU。但是它的提示只有在你打開鎖屏或者進行充電時才會進行提示。)


優化總結


性能優化是我們進階的畢竟之路。So,我們必須要會,至於「會」到什麼程度,就要看個人理解了。其實,上面介紹的只是性能問題的冰山一角,真正的優化,我們是在項目中總結出來的。但,我們不能一味的追求優化,就例如我,現在只是在進行優化的總結,而對於真正的實行,並沒有開始,因為,優化是有風險的,一個不小心,整個項目都可能炸了。所以這就需要你的經驗,以及各種總結,在改進行優化的地方先進行優化,看看效果如何,例如,UI的優化以及代碼的優化。可以先拿一些網上的開源項目進行優化等等。最後,盡情的享受優化吧。


文章參考:


1. Android藝術探索


2. Android應用開發性能優化完全分析


3. 性能優化典範


4. 雙十二技術哥


5. Google官方優化視頻


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

首日票申請通道緊急關閉,CCTC 2017雲計算技術大會通票限量發售,搶票倒計時!
從 0到1 開啟智能化硬體開發
使用TCP時序圖解釋BBR擁塞控制演算法的幾個細節
全球WannaCry勒索病毒爆發背後的技術漏洞

TAG:CSDN |

您可能感興趣

Android Studio項目模板全面解析
Illustrator詳細解析混合工具的使用方法
Wish ProductBoost智能版全面解析:旺季PB,認準這17種優勢產品
藉助Jackson的JsonTypeInfo註解實現多態類的解析
阿里媽媽基於TensorFlow做了哪些深度優化?TensorFlowRS架構解析
本田Clarity Electric功能、內外飾全解析 低續航里程成缺憾
技術解析系列PouchContainer Goroutine Leak 檢測實踐
Ansible 深度解析
Oracle集群軟體全解析
Photoshop詳細解析調色工具使用方法
深度解析政府監聽工具FinFisher:Windows
fileless malware技術解析及檢測
華為CloudNative分散式資料庫技術解析
深度解析華為matebook X Pro為何能如此出色
Bayesian Personalized Ranking 演算法解析及Python實現
英特爾Whiskey Lake處理器性能解析
Grasshopper曲面玻璃平板化解析
Google 開源 robots.txt 解析器,推動 REP 標準化
AtomicInteger 源碼解析
Moto E5 Play Android Go手機發布:真上古解析度