Android介面效能調優手冊

來源:互聯網
上載者:User

標籤:去除   不能   base   lis   協助   選擇   做了   分割   graphic   

介面是 Android 應用中直接影響使用者體驗最關鍵的部分。如果代碼實現得不好,介面容易發生卡頓且導致應用佔用大量記憶體。我司這類做 ROM 的公司更不一樣,預裝的應用一定要非常流暢,這樣給客戶或使用者的第一感覺就是快。又卡又慢的應用體驗,會影響客戶或使用者對產品的信心和評價,所以不可忽視。

一. Android渲染知識 1.1 繪製原理

  
Android系統要求每一幀都要在 16ms 內繪製完成,平滑的完成一幀意味著任何特殊的幀需要執行所有的渲染代碼(包括 framework 發送給 GPU 和 CPU 繪製到緩衝區的命令)都要在 16ms 內完成,保持流暢的體驗。這個速度允許系統在動畫和輸入事件的過程中以約 60 幀每秒( 1秒 / 0.016幀每秒 = 62.5幀/秒 )的平滑幀率來渲染。


  
如果你的應用沒有在 16ms 內完成這一幀的繪製,假設你花了 24ms 來繪製這一幀,那麼就會出現掉幀的情況。


  
系統準備將新的一幀繪製到螢幕上,但是這一幀並沒有準備好,所有就不會有繪製操作,畫面也就不會重新整理。反饋到使用者身上,就是使用者盯著同一張圖看了 32ms 而不是 16ms ,也就是說掉幀發生了。

1.2 掉幀

  
掉幀是使用者體驗中一個非常核心的問題。丟棄了當前幀,並且之後不能夠延續之前的幀率,這種不連續的間隔會容易會引起使用者的注意,也就是我們常說的卡頓、不流暢。
  
引起掉幀的原因非常多,比如:

  • 花了非常多時間重新繪製介面中的大部分東西,這樣非常浪費CPU周期;

  • 過度繪製嚴重,在繪製使用者看不到的對象上花費了太多的時間;

  • 有一大堆動畫重複了一遍又一遍,消耗 CPU 、 GPU 資源;

  • 頻繁的觸發記憶體回收;

1.3 為什麼是60Fps?

Android系統要求每一幀都要在 16ms 內繪製完成,那麼1秒的幀率就是約 60 幀每秒( 1秒 / 0.016幀每秒 = 62.5幀/秒 ),那為什麼要以 60 Fps來作為 App 效能的衡量標準呢?這是因為人眼和大腦之間的協作無法感知到超過 60 Fps的畫面更新。

市面上絕大多數Android裝置的螢幕重新整理頻率是 60 HZ。當然,超過 60 Fps 是沒有意義的,人眼感知不到區別。24 Fps 是人眼能感知的連續線性運動,所以是電影膠圈的常用幀率,因為這個幀率已經足夠支撐大部分電影畫面所要表達的內容,同時能最大限度地減少費用支出。但是,低於 30 Fps 是無法順暢表現絢麗的畫面內容的,此時就需要用到 60 Fps 來達到想要表達的效果。
  
應用的介面效能目標就是保持 60 Fps,這意味著每一幀你只有 16 ms(1秒 / 60幀率)的時間來處理所有的任務。

1.4 記憶體回收

  
記憶體回收行程是一個在應用運行期間自動釋放那些不再引用的記憶體的機制,常稱 GC 。頻繁的 GC 也是導致嚴重性能問題的罪魁禍首之一。
前面提到,平滑的完成一幀意味著所有渲染代碼都必須在 16ms 內完成。頻繁的 GC 會嚴重限制一幀時間內的剩餘時間,如果 GC 所做的工作超過了那些必須的工作,那麼留給應用平滑的幀率的時間就越少。越接近 16ms ,在記憶體回收事件觸發的時候,就越容易導致卡頓。
  
注意,Android4.4 引進了新的 ART 虛擬機器來取代 Dalvik 虛擬機器。它們的機制大有不同,簡單而言:

  • Dalvik 虛擬機器的 GC 是非常耗資源的,並且在正常的情況下一個硬體效能不錯的Android裝置也會很容易耗費掉 10 – 20 ms 的時間;
  • ART 虛擬機器的GC會動態提升記憶體回收的效率,在 ART 中的中斷,通常在 2 – 3 ms 間。 比 Dalvik 虛擬機器有很大的效能提升;  
    ART 虛擬機器相對於 Dalvik 虛擬機器來說的記憶體回收來說有一個很大的效能提升,但 2 – 3 ms 的回收時間對於超過16ms幀率的界限也是足夠的。因此,儘管記憶體回收在 Android 5.0 之後不再是耗資源的行為,但也是始終需要儘可能避免的,特別是在執行動畫的情況下,可能會導致一些讓使用者明顯感覺的丟幀。### ** 1.5 UI 線程**  
    UI 線程是應用的主線程,很多的效能和卡頓問題是由於我們在主線程中做了大量的工作。  
    所以,所有耗資源的操作,比如 IO 操作、網路操作、SQL 操作、列表重新整理等,都應該用後台進程去實現,不能佔用主線程,主線程是 UI 線程,是保持程式流暢的關鍵;  
    在 Android 5.0 版本裡,Android 架構層引入了 “ Render Thread ” ,用於向 GPU 發送實際渲染的操作。這個線程減輕了一些 UI 線程減少的操作。但是輸入、滾動和動畫仍然在 UI thread,因為 Thread 必須能夠響應操作。
1.6 垂直同步

  
垂直同步是 Android4.1 通過 Project Butter 在 UI 架構中引入的新技術,同期引入的還有 Triple Buffer 和 HWComposer 等技術,都是為提高 UI 的流暢性而生。
  
舉個例子,你拍了一張照片,然後旋轉5度再拍另外一張照片,將兩照片的中間剪開並拼接在一起,得到:
中間這部分有明顯區別的部分,等價於裝置重新整理率和畫面播放速率不一致的結果。
  
一般而言, GPU 的畫面播放速率應高於重新整理率,才不會卡頓或掉幀。如果螢幕重新整理率比畫面播放速率還快,螢幕會在兩幀中顯示同一個畫面,這種斷斷續續情況持續發生時,使用者將會很明顯地感覺到動畫的卡頓或者掉幀,然後又恢複正常,我們常稱之為閃屏、跳幀、延遲。應用應避免這些幀率下降的情況,以確保 GPU 能在螢幕重新整理之前完成資料的擷取及寫入,保證動畫流暢。

1.7 UI 繪製機制與柵格化

  
絕大多數渲染操作都依賴兩個硬體: CPU 、 GPU 。 CPU 負責 Measure 、 layout 、 Record 、 Execute 的計算操作, GPU 負責柵格化( Rasterization )操作。 非必需的視圖組件會帶來多餘的 CPU 計算操作,還會佔用多餘的 GPU 資源。
  
柵格化( Rasterization )能將 Button 、 Shape 、 Path 、 Bitmap 等資源群組件拆分到不同的像素上進行顯示。這個操作很費時,所以引入了 GPU 來加快柵格化的操作。
  
CPU 負責把 UI 組件計算成多邊形( Polygons ),紋理( Texture ),然後交給 GPU 進行柵格化渲染,再將處理結果傳到螢幕上顯示
  
在 Android 裡的那些資源群組件的顯示(比如 Bitmaps 、 Drawable ),都是一起打包到統一的紋理( Texture )當中,然後再傳遞到 GPU 裡面。
  
圖片的顯示,則是先經過 CPU 的計算載入到記憶體中,再傳給 GPU 進行渲染。文字的顯示,則是先經過 CPU 換算成紋理( Texture ),再傳給 GPU 進行渲染,返回到 CPU 繪製單個字元的時候,再重新引用經過 GPU 渲染的內容。動畫的顯示更加複雜,我們需要在 16 ms 內處理完所有 CPU 和 GPU 的計算、繪製、渲染等操作,才能獲得應用的流暢體驗。

二. To檢測和解決 2.1 檢測維度  

根據業務的不同與所需要的測試粒度的不同,就會有不同的檢測維度。目前我所在業務所需的介面效能檢測維度如下:

  • 介面過度繪製;(檢測過度繪製)
  • 渲染效能;(檢測strict 模式下的UI渲染效能呈現)
  • 布局邊界合理性;(檢測元素顯示的合理性)
  • 還有專項測試中某些使用者情境可能還包含著另外一些隱形的檢測維度,比如:
  • OpenGL 跟蹤分析;
  • GPU 視圖更新合理性;
  • Flash 硬體層更新合理性;
  • 動畫加 / 減速狀態問題點檢測;
  • ……
2.2 調試工具

  
檢測和解決介面效能問題很大程度上依賴於你的應用程式架構,幸運的是,Andorid 提供了很多調試工具,知道並學會使用這些工具很重要,它們可以協助我們調試和分析介面效能問題,以讓應用擁有更好的效能體驗。下面列舉Android常見的介面效能調試工具:

2.2.1 Hierarchy View

  
Hierarchy View 在Android SDK裡內建,常用來查看介面的視圖結構是否過於複雜,用於瞭解哪些視圖過度繪製,又該如何進行改進

2.2.2 Lint  

Lint 是 ADT 內建的靜態代碼掃描工具,可以給 XML 布局檔案和 項目代碼中不合理的或存在風險的模組提出改善性建議。官方關於 Lint 的實際使用的提示,列舉幾點如下:

  • 包含無用的分支,建議去除;
  • 包含無用的父控制項,建議去除;
  • 警告該布局深度過深;
  • 建議使用 compound drawables ;
  • 建議使用 merge 標籤;……
2.2.3 Systrace

  
Systrace 在Android DDMS 裡內建,可以用來跟蹤 graphics 、view 和 window 的資訊,發現一些深層次的問題。很麻煩,限制大,實際調試中我基本用不到。

2.2.4 Track

  
Track 在 Android DDMS裡內建,是個很棒的用來跟蹤構造視圖的時候哪些方法費時,精確到每一個函數,無論是應用函數還是系統函數,我們可以很容易地看到掉幀的地方以及那一幀所有函數的調用情況,找出問題點進行最佳化。

2.2.5 OverDraw

  
通過在 Android 裝置的設定 APP 的開發人員選項裡開啟 “ 調試 GPU 過度繪製 ” ,來查看應用所有介面及分支介面下的過度繪製情況,方便進行最佳化。

2.2.6 GPU 呈現模式分析

  
通過在 Android 裝置的設定 APP 的開發人員選項裡啟動 “ GPU 呈現模式分析 ” ,可以得到最近 128 幀 每一幀渲染的時間,分析效能渲染的效能及效能瓶頸。

2.2.7 StrictMode  

通過在 Android 裝置的設定 APP 的開發人員選項裡啟動 “ strict 模式 ” ,來查看應用哪些操作在主線程上執行時間過長。當一些操作違背了strict 模式時螢幕的四周邊界會閃爍紅色,同時輸出 StrictMode 的相關資訊到 LOGCAT 日誌中。

2.2.8 Animator duration scale  

通過在 Android 裝置的設定 APP 的開發人員選項裡開啟 “ 視窗動畫縮放 ” / “ 過渡動畫縮放 ” / “ 動畫程式時間長度縮放 ”,來加速或減慢動畫的時間,以查看加速或減慢狀態下的動畫是否會有問題。

2.3 如何解決  

前面提到過我司的目前所需的測試維度如下:

  • 介面過度繪製;(檢測過度繪製)
  • 渲染效能;(檢測strict 模式下的UI渲染效能呈現)故接下來將圍繞這兩點,分別從概念、追蹤、挖掘根源以及排查的工具來具體講述如何解決,以及給開發的最佳化建議。
三. 介面過度繪製(OverDraw) 3.1 過度繪製概念

  
過度繪製是一個術語,表示某些組件在螢幕上的一個像素點的繪製次數超過 1 次。
  
通俗來講,繪製介面可以類比成一個塗鴉客塗鴉牆壁,塗鴉是一件工作量很大的事情,牆面的每個點在塗鴉過程中可能被塗了各種各樣的顏色,但最終呈現的顏色卻只可能是 1 種。這意味著我們花大力氣塗鴉過程中那些非最終呈現的顏色對路人是不可見的,是一種對時間、精力和資源的浪費,存在很大的改善空間。繪製介面同理,花了太多的時間去繪製那些堆疊在下面的、使用者看不到的東西,這樣是在浪費CPU周期和渲染時間!
  
官方例子,被使用者啟用的卡片在最上面,而那些沒有啟用的卡片在下面,在繪製使用者看不到的對象上花費了太多的時間。

3.2 追蹤過度繪製

  
通過在 Android 裝置的設定 APP 的開發人員選項裡開啟 “ 調試 GPU 過度繪製 ” ,來查看應用所有介面及分支介面下的過度繪製情況,方便進行最佳化。
  
Android 會在螢幕上顯示不同深淺的顏色來表示過度繪製:

  • 沒顏色:沒有過度繪製,即一個像素點繪製了 1 次,顯示應用本來的顏色;
  • 藍色:1倍過度繪製,即一個像素點繪製了 2 次;
  • 綠色:2倍過度繪製,即一個像素點繪製了 3 次;
  • 淺紅色:3倍過度繪製,即一個像素點繪製了 4 次;
  • 深紅色:4倍過度繪製及以上,即一個像素點繪製了 5 次及以上;裝置的硬體效能是有限的,當過度繪製導致應用需要消耗更多資源(超過了可用資源)的時候效能就會降低,表現為卡頓、不流暢、ANR 等。為了最大限度地提高應用的效能和體驗,就需要儘可能地減少過度繪製,即更多的藍色色塊而不是紅色色塊。  
    實際測試,常用以下兩點來作為過度繪製的測試單位,將過度繪製控制在一個約定好的合理範圍內:
  • 應用所有介面以及分支介面均不存在超過4X過度繪製(深紅色地區);
  • 應用所有介面以及分支介面下,3X過度繪製總面積(淺紅色地區)不超過螢幕可視地區的1/4;
3.3 過度繪製的根源

  
過度繪製很大程度上來自於視圖相互重疊的問題,其次還有不必要的背景重疊。
  
官方例子,比如一個應用所有的View都有背景的話,就會看起來像第一張圖中那樣,而在去除這些不必要的背景之後(指的是Window的預設背景、Layout的背景、文字以及圖片的可能存在的背景),效果就像第二張圖那樣,基本沒有過度繪製的情況。

3.4 不合理的xml布局對繪製的影響

  
當布局檔案的節點樹的深度越深,XML 中的標籤和屬性設定越多,對介面的顯示有災難性影響。
  
一個介面要顯示出來,第一步會進行解析布局,在 requestLayout 之後還要進行一系列的 measure 、 layout 、 draw 操作,若布局檔案嵌套過深、擁有的標籤屬性過於臃腫,每一步的執行時間都會受到影響,而介面的顯示是進行完這些操作後才會顯示的,所以每一步操作的時間增長,最終顯示的時間就會越長。

3.5 源碼相關

  
有能力且有興趣看源碼的童鞋,過度繪製的源碼位置在: /frameworks/base/libs/hwui/OpenGLRenderer.cpp ,有興趣的可以去研究查看。

四. 渲染效能(Rendering) 4.1 渲染效能概念

  
渲染效能往往是掉幀的罪魁禍首,這種問題很常見,讓人頭疼。好在 Android 給我們提供了一個強大的工具,協助我們非常容易追蹤效能渲染問題,看到究竟是什麼導致你的應用出現卡頓、掉幀。

4.2 追蹤渲染效能

  
通過在 Android 裝置的設定 APP 的開發人員選項裡開啟 “ GPU 呈現模式分析 ” 選項,選擇 ” 在螢幕上顯示為橫條圖 “ 。
  
這個工具會在Android 裝置的螢幕上即時顯示當前介面的最近 128 幀 的 GPU 繪製圖形資料,包括 StatusBar 、 NavBar 、 當前介面的 GPU 繪製圖形柱狀圖資料。我們一般只需關心當前介面的 GPU 繪製圖形資料即可。
  
介面上一共有 128 個小柱狀圖,代表的是當前介面最近的 128 幀 GPU 繪製圖形資料。一個小柱狀圖代表的這一幀畫面渲染的耗時,柱狀圖越高代表耗時越長。隨著介面的重新整理,柱狀圖資訊也會即時滾動重新整理。
  
中間有一條綠線,代表 16 ms ,保持動畫流暢的關鍵就在於讓這些垂直的柱狀條儘可能地保持在綠線下面,任何時候超過綠線,你就有可能丟失一幀的內容。
  
每一個柱狀圖都是由三種顏色構成:藍、紅、黃。

  • 藍色代表的是這一幀繪製 Display List 的時間。通俗來說,就是記錄了需要花費多長時間在螢幕上更新視圖。用代碼語言來說,就是執行視圖的 onDraw 方法,建立或更新每一個視圖的 Display List 的時間。
  • 紅色代表的是這一幀 OpenGL 渲染 Display List 所需要的時間。通俗來說,就是記錄了執行視圖繪製的耗時。用代碼語言來說,就是 Android 用 OpenGL ES 的 API 介面進行 2D 渲染 Display List 的時間。
  • 黃色代表的是這一幀 CPU 等待 GPU 處理的時間。通俗來說,就是 CPU 等待 GPU 發出接到命令的回複的等待時間。用代碼語言來說,就是這是一個阻塞調用。  
    實際測試,常用以下兩點來作為渲染效能的測試單位,將渲染效能控制在一個約定好的合理範圍內:
  • 執行應用的所有功能及分支功能,操作過程中涉及的柱狀條地區應至少 90 % 保持到綠線下面;
  • 從使用者體檢的角度主觀判斷應用在 512 M 記憶體的 Android 裝置下所有操作過程中的卡頓感是否能接受,不會感覺突兀怪異;
4.3 渲染效能差的根源

  
當你看到藍色的線較高的時候,可能是由於你的視圖突然無效了需要重新繪製,或者是自訂的視圖過於複雜耗時過長。
  
當你看到紅色的線較高的時候,可能是由於你的視圖重新提交了需要重新繪製導致的(比如螢幕從豎屏旋轉成橫屏後當前介面重新建立),或者是自訂的視圖很複雜,繪製起來很麻煩,導致耗時過長。比如下面這種視圖:
  
當你看到黃色的線較高的時候,那就意味著你給 GPU 太多的工作,太多的負責視圖需要 OpenGL 命令去繪製和處理,導致 CPU 遲遲沒等到 GPU 發出接到命令的回複。

4.4 檢測說明

這個工具能夠很好地協助你找到渲染相關的問題,協助你找到卡頓的效能瓶頸,追蹤究竟是什麼導致被測應用出現卡頓、變慢的情況,以便在代碼層面進行最佳化。甚至讓負責產品設計的人去改善他的設計,以獲得良好的使用者體驗。
  
檢測渲染效能時,常伴隨著開啟“ strict 模式 ” 查看應用哪些情景在 UI 線程(主線程)上執行時間過長。
  
另外有些強大但可能少用的工具在測試效能渲染時輔助分析,比如:

  • HierarchyViewer:這個工具常用來查看介面的視圖結構是否過於複雜,用於瞭解哪些視圖過度繪製,又該如何進行改進;
  • Tracer for OpenGL:這個工具收集了所有UI介面發給GPU的繪製命令。常用於輔助開發人員 DEBUG 、定位一些 HierarchyViewer 工具定位不了的疑難渲染細節問題。
五. 給開發的介面最佳化 Advice 5.1 最佳化布局的結構

  
布局結構太複雜,會減慢渲染的速度,造成效能瓶頸。我們可以通過以下這些慣用、有效布局原則來最佳化:

  • 避免複雜的View層級。布局越複雜就越臃腫,就越容易出現效能問題,尋找最節省資源的方式去展示嵌套的內容;
  • 盡量避免在視圖層級的頂層使用相對布局 RelativeLayout 。相對布局 RelativeLayout 比較耗資源,因為一個相對布局 RelativeLayout 需要兩次度量來確保自己處理了所有的布局關係,而且這個問題會伴隨著視圖層級中的相對布局 RelativeLayout 的增多,而變得更嚴重;
  • 布局層級一樣的情況建議使用線性布局 LinearLayout 代替相對布局 RelativeLayout,因為線性布局 LinearLayout 效能要更高一些;確實需要對分支進行相對布局 RelativeLayout 的時候,可以考慮更最佳化的網格布局 GridLayout ,它已經預先處理了分支視圖的關係,可以避免兩次度量的問題;
  • 相對複雜的布局建議採用相對布局 RelativeLayout ,相對布局 RelativeLayout 可以簡單實現線性布局 LinearLayout 嵌套才能實現的布局;
  • 不要使用絕對布局 AbsoluteLayout ;
  • 將可重複使用的組件抽取出來並用 標籤進行重用。如果應用多個地方的 UI 用到某個布局,就將其寫成一個布局組件,便於各個 UI 重用。
  • 使用 merge 標籤減少布局的嵌套層次。
  • 去掉多餘的不可見背景。有多層背景顏色的布局,只留最上層的對使用者可見的顏色即可,其他使用者不可見的底層顏色可以去掉,減少無效的繪製操作;
  • 盡量避免使用 layoutweight 屬性。使用包含 layoutweight 屬性的線性布局 LinearLayout 每一個子組件都需要被測量兩次,會消耗過多的系統資源。在使用 ListView 標籤與 GridView 標籤的時候,這個問題顯的尤其重要,因為子組件會重複被建立。平分布局可以使用相對布局 RelativeLayout 裡一個 0dp 的 view 做分割線來搞定,如果不行,那就……;
  • 合理的介面的布局結構應是寬而淺,而不是窄而深;
5.2 最佳化處理邏輯
  • 按需載入視圖。某些不怎麼重用的耗資源檢視,可以等到需要的時候再載入,提高UI渲染速度;
  • 使用 ViewStub 標籤來載入一些不常用的布局;
  • 動態地 inflation view 效能要比用 ViewStub 標籤的 setVisiblity 效能要好,當然某些功能的實現採用 ViewStub 標籤更合適;
  • 盡量避免不必要的耗資源操作,節省寶貴的運算時間;
  • 避免在 UI 線程進行繁重的操作。耗資源的操作(比如 IO 操作、網路操作、SQL 操作、列表重新整理等)耗資源的操作應用後台進程去實現,不能佔用 UI 線程,UI 線程是主線程,主線程是保持程式流暢的關鍵,應該只操作那些核心的 UI 操作,比如處理視圖的屬性和繪製;
  • 最小化喚醒機制。我們常用廣播來接收那些期望響應的訊息和事件,但過多的響應超過本身需求的話,會消耗多餘的 Android 裝置效能和資源。所以應該最小化喚醒機制,當應用不關心這些消失和事件時,就關閉廣播,並謹慎選擇那些要響應的 Intent 。
  • 為低端裝置考慮,比如 512M 記憶體、雙核 CPU 、低解析度,確保你的應用可以滿足不同水平的裝置。
  • 最佳化應用的啟動速度。當應用啟動一個應用時,介面的儘快反饋顯示可以給使用者一個良好的體驗。為了啟動更快,可以消極式載入一些 UI 以及避免在應用 Application 層級初始化代碼。
5.3 善用 DEBUG 工具
  • 多使用Android提供的一些調試工具去追蹤應用主要功能的效能情況;
  • 多使用Android提供的一些調試工具去追蹤應用主要功能的記憶體配置情況;

 

本文轉載自聽雲部落格

Android介面效能調優手冊

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.