Android app優化之導致app 卡頓慢的直接原因

大多數用戶感知到的卡頓等性能問題的最主要根源都是因爲渲染性能。從設計師的角度,他們希望App能夠有更多的動畫,圖片等時尚元素來實現流暢的用戶體驗。但是Android系統很有可能無法及時完成那些複雜的界面渲染操作。Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,爲了能夠實現60fps,這意味着程序的大多數操作都必須在16ms內完成()時間超出16ms越多,丟的幀就越多,可以大概估計一下Android 5秒沒響應拋出anr異常期間丟了多少幀,筆者算了312幀)。

 

如果你的某個操作花費時間是24ms,系統在得到VSYNC信號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那麼用戶在32ms內看到的會是同一幀畫面。


用戶容易在UI執行動畫或者滑動ListView的時候感知到卡頓不流暢,是因爲這裏的操作相對複雜,容易發生丟幀的現象,從而感覺卡頓。有很多原因可以導致丟幀,也許是因爲你的layout太過複雜,無法在16ms內完成渲染,有可能是因爲你的UI上有層疊太多的繪製單元,還有可能是因爲動畫執行的次數過多。這些都會導致CPU或者GPU負載過重。

我們可以通過一些工具來定位問題,比如可以使用HierarchyViewer來查找Activity中的佈局是否過於複雜,也可以使用手機設置裏面的開發者選項,打開Show GPU Overdraw等選項進行觀察。你還可以使用TraceView來觀察CPU的執行情況,更加快捷的找到性能瓶頸(所以佈局有一個原則,就是儘量用簡單的佈局)。

2) Understanding Overdraw(理解過度繪製)

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


當設計上追求更華麗的視覺效果的時候,我們就容易陷入採用越來越多的層疊組件來實現這種視覺效果的怪圈。這很容易導致大量的性能問題,爲了獲得最佳的性能,我們必須儘量減少Overdraw的情況發生。

幸運的是,我們可以通過手機設置裏面的開發者選項,打開Show GPU Overdraw的選項,可以觀察UI上的Overdraw情況。


藍色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況,我們的目標就是儘量減少紅色Overdraw,看到更多的藍色區域(藍色表面該區域在同一幀的時間裏被繪製了一次,淡綠表明兩次,往後一次遞增)。

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

3) Understanding VSYNC

爲了理解App是如何進行渲染的,我們必須瞭解手機硬件是如何工作,那麼就必須理解什麼是VSYNC。

在講解VSYNC之前,我們需要了解兩個相關的概念:

· Refresh Rate:代表了屏幕在一秒內刷新屏幕的次數,這取決於硬件的固定參數,例如60Hz。

· Frame Rate:代表了GPU在一秒內繪製操作的幀數,例如30fps,60fps。


GPU會獲取圖形數據進行渲染,然後硬件負責把渲染後的內容呈現到屏幕上,他們兩者不停的進行協作。


不幸的是,刷新頻率和幀率並不是總能夠保持相同的節奏。如果發生幀率與刷新頻率不一致的情況,就會容易出現Tearing的現象(畫面上下兩部分顯示內容發生斷裂,來自不同的兩幀數據發生重疊)。



通常來說,幀率超過刷新頻率只是一種理想的狀況,在超過60fps的情況下,GPU所產生的幀數據會因爲等待VSYNC的刷新信息而被Hold住,這樣能夠保持每次刷新都有實際的新的數據可以顯示。但是我們遇到更多的情況是幀率小於刷新頻率。

在這種情況下,某些幀顯示的畫面內容就會與上一幀的畫面相同。糟糕的事情是,幀率從超過60fps突然掉到60fps以下,這樣就會發生LAG,JANK,HITCHING等卡頓掉幀的不順滑的情況。這也是用戶感受不好的原因所在(所以幀率可以快不能慢,快不會影響渲染機制,但是慢的話就會丟幀,當然不是丟幀都會影響用戶體驗(出現卡,頓,慢),其實在沒出現卡頓慢的現象前就可能已經丟幀了,只是用戶還沒發覺,發覺是由於丟幀很嚴重了)。

4) Tool:Profile GPU Rendering

性能問題如此的麻煩,幸好我們可以有工具來進行調試。打開手機裏面的開發者選項,選擇Profile GPU Rendering,選中On screen as bars(有的手機可能沒有這個選項,可能是由於你手機系統被手機廠商定製過,將這個選項刪了,這種情況推薦使用第三方模擬器,或者使用別的手機調試)的選項。

選擇了這樣以後,我們可以在手機畫面上看到豐富的GPU繪製圖形信息,分別關於StatusBar,NavBar,激活的程序Activity區域的GPU Rending信息。


隨着界面的刷新,界面上會滾動顯示垂直的柱狀圖來表示每幀畫面所需要渲染的時間,柱狀圖越高表示花費的渲染時間越長。

中間有一根綠色的橫線,代表16ms,我們需要確保每一幀花費的總時間都低於這條橫線,這樣才能夠避免出現卡頓的問題。


每一條柱狀線都包含三部分,藍色代表測量繪製Display List(解析xml的佈局文件)的時間,紅色代表OpenGL渲染Display List所需要的時間,黃色代表CPU等待GPU處理的時間。

 

 

5) Why 60fps?(爲什麼幀率要選60fps,不能少些嗎?)

我們通常都會提到60fps與16ms,可是知道爲何會是以程序是否達到60fps來作爲App性能的衡量標準嗎?這是因爲人眼與大腦之間的協作無法感知超過60fps的畫面更新(60fps是最好的效果)。

12fps大概類似手動快速翻動書籍的幀率,這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這其實是歸功於運動模糊的效果。24fps是電影膠圈通常使用的幀率,因爲這個幀率已經足夠支撐大部分電影畫面需要表達的內容,同時能夠最大的減少費用支出。但是低於30fps是無法順暢表現絢麗的畫面內容的,此時就需要用到60fps來達到想要的效果,當然超過60fps是沒有必要的。

開發app的性能目標就是保持60fps(低一些亦不會影響用戶體驗),這意味着每一幀你只有16ms=1000/60的時間來處理所有的任務(這個時間可以用於與traceView 得到的時間對比,不能超出太多例如大於32ms,否則就該優化了)。

6) Android, UI and the GPU

瞭解Android是如何利用GPU進行畫面渲染有助於我們更好的理解性能問題。那麼一個最實際的問題是:activity的畫面是如何繪製到屏幕上的?那些複雜的XML佈局文件又是如何能夠被識別並繪製出來的?


Resterization柵格化是繪製那些Button,Shape,Path,String,Bitmap等組件最基礎的操作。它把那些組件拆分到不同的像素上進行顯示。這是一個很費時的操作,GPU的引入就是爲了加快柵格化的操作。

CPU負責把UI組件計算成Polygons,Texture紋理,然後交給GPU進行柵格化渲染。

然而每次從CPU轉移到GPU是一件很麻煩的事情,所幸的是OpenGL ES可以把那些需要渲染的紋理Hold在GPU Memory裏面,在下次需要渲染的時候直接進行操作。所以如果你更新了GPU所hold住的紋理內容,那麼之前保存的狀態就丟失了。


在Android裏面那些由主題所提供的資源,例如Bitmaps,Drawables都是一起打包到統一的Texture紋理當中,然後再傳遞到GPU裏面,這意味着每次你需要使用這些資源的時候,都是直接從紋理裏面進行獲取渲染的。當然隨着UI組件的越來越豐富,有了更多演變的形態。例如顯示圖片的時候,需要先經過CPU的計算加載到內存中,然後傳遞給GPU進行渲染。文字的顯示更加複雜,需要先經過CPU換算成紋理,然後再交給GPU進行渲染,回到CPU繪製單個字符的時候,再重新引用經過GPU渲染的內容。動畫則是一個更加複雜的操作流程。

爲了能夠使得App流暢,我們需要在每一幀16ms以內處理完所有的CPU與GPU計算,繪製,渲染等等操作,重點是減少cup的時間,因爲我們寫的程序是在cup直接執行的,和Gpu就關係較遠,一般不考慮。

總結一下,這裏是介紹了導致我們app卡頓慢的直接原因,也是底層原因,至於別的原因例如內存泄漏,耗時任務在主線程這些問題都是先影響了Android渲染機制。然後纔會出現卡頓慢現象的,準確說是影響到渲染機制cup相關部分導致延後運行。知道了這個原因後,優化的具體措施就有了。

最後出於尊重別人勞動成果的目的,特別說明一下:關於本系列博客是筆者在看了另一個人的博客後寫的,所以或多或少會有一些影子

那個人的博客地址 :

http://hukai.me/android-performance-patterns/  他的博客,筆者感覺很有用。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章