刷新率:每秒屏幕刷新次數。
幀率:GPU 在壹秒內繪制的幀數。
雖然現在有的廠商推出了高刷新率的手機,但是主流的還是 60Hz,即1秒顯示60幀,1000ms / 60 frames ≈ 16.67 ms/frames,為了保證 App 的流暢度,我們應該盡量讓每幀的繪制時間不超過 16ms。
Android 的顯示過程可以簡單概括為:應用程序把經過 measure(測量)、layout(布局)、draw(繪制)後的 surface 緩存數據,通過 SurfaceFlinger 把數據渲染到顯示屏幕上,通過 Android 的刷新機制來刷新數據。換言之,應用層負責繪制,系統層負責渲染,通過進程間通信把應用層需要繪制的數據傳遞到系統層服務,系統層通過刷新機制把數據更新到屏幕上。
以下是有關概念的解釋:
在 Android 中每個 view 都會經過 measure 和 layout 來確定其所在的大小和位置,然後繪制到 surface (緩沖區上),繪制是由 ViewRootImpl 類中 performTraversals() 方法發起的。
Android支持兩種繪制方式: 和 。硬件極速從 Android 3.0 開始支持,它在 UI 顯示和繪制效率方面遠高於軟件繪制,但是它的也有缺點:
經過多次繪制後,要顯示的 view 相關的數據存儲(如大小和位置)在 Surface 的緩沖區中,接下來渲染操作交由系統進程中的 SurfaceFlinger 服務來完成,這是壹個 IPC(進程間通信)過程。SurfaceFlinger 的主要工作流程如下:
當 Android 應用層在圖形緩沖區中繪制好 View 層次結後,應用層通過 Binder 機制與 SurfaceFlinger 通信並借助壹塊匿名***享內存把圖形緩沖區交給 SurfaceFlinger 服務。由於單純的匿名***享服務在傳遞多個窗口數據時缺乏有效的管理,所以匿名***享內存就被抽象為壹個更上層的數據結構——SharedClient,在 SharedClient 中,最多有 31 個 SharedBufferStack,每個 SharedBufferStack 都對應壹個 Surface 即壹個 Window。 這表明壹個 Android 應用程序最多可以包含 31 個 window 。
繪制的過程首先是 CPU 準備數據(measure、layout等),GPU 負責柵格化、渲染。因為圖像 API 不允許 CPU 直接與 GPU 通信,所以要通過壹個圖形驅動的中間層來進行連接。圖形驅動裏面維護了壹個隊列,CPU 把 display list(待顯示的數據列表)添加到隊列中,GPU 從這個隊列中取出數據進行繪制,最終在屏幕上顯示出來,如下圖所示:
Android 系統每隔 16ms 會發出 VSYNC 信號,觸發對 UI 進行渲染,如果每次都渲染成功,就能夠達到流暢畫面所需的 60PS。
雙緩沖顧名思義是有兩個緩沖區(上文提到的 SharedBufferStack),分別是 FontBuffer(又叫作 FrameBuffer) 和 BackBuffer。UI 總是先在 Back Buffer 中繪制,然後再和 Font Buffer 交換,渲染到顯示設備中,即只有當另壹個 buffer 的數據準備好後,才會通過系統調用來通知顯示設備切換 Buffer。
雙緩沖機制在大部分情況下是適用的,但是如果某個環節出現了問題,CPU 資源就有可能存在浪費,如下圖所示:
VSYNC 類似與時鐘中斷。豎線分割的部分代表 16ms 的時間段。正常情況下,在每壹時間段內,Display 顯示壹幀數據(即每秒60幀)。
上圖中在第二個 16ms 時間段內,Display 本應顯示 B 幀,但是因為 GPU 還在處理 B 幀,導致 A 幀被重復顯示。與此同時,在第二個時間段內,處於 CPU 處於空閑狀態,造成了浪費。因為 A Buffer 被 Diaplay 在使用(SufaceFlinger 用完後不會釋放當前的 Buffer,只會釋放舊的 Buffer),B Buffer 被 GPU 在使用,這就是 雙緩沖機制的局限性。
Android 4.1 版本中對 Android Display 系統進行了重構,引入了三個核心元素:
在第二個 16ms 時間內,CPU 使用 C Buffer 繪圖,雖然還是會多顯示 A 幀壹次,但是後續的顯示相對雙緩沖機制就順滑多了。但是 Buffer 並不是越多越好,從上圖可知,在第二個時間內,CPU 繪制的第 C 幀數據要到第四個 16ms 才能顯示,這比雙 Buffer 多了 16ms 的延遲。由此可見,雙緩沖保證低時延,三緩沖保證穩定性。
整個流程簡單來說就是 CPU/GPU 會接收到 VSYNC 信號,觸發對 UI 進行渲染(每 16ms 顯示壹幀)。 在 16ms 內需要完成兩項任務:將 UI 對象轉換為壹系列多邊形和紋理(柵格化)和 CPU 傳遞處理數據到 GPU ,更詳細的內容可以看這篇文章 Android的16ms和垂直同步以及三重緩存 。
了解 Android 繪制流程後,我們不難反推 Android 應用程序卡頓的原因: