1. 程式人生 > >Android app優化之導致app 卡頓慢的直接原因

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/  他的部落格,筆者感覺很有用。