Android 繪制與 16ms 不得不說(shuō)的故事

刷新率、幀率

刷新率:每秒屏幕刷新次數(shù)。
幀率:GPU 在一秒內(nèi)繪制的幀數(shù)。
雖然現(xiàn)在有的廠商推出了高刷新率的手機(jī),但是主流的還是 60Hz,即1秒顯示60幀,1000ms / 60 frames ≈ 16.67 ms/frames,為了保證 App 的流暢度,我們應(yīng)該盡量讓每幀的繪制時(shí)間不超過(guò) 16ms。

Android 系統(tǒng)顯示原理

Android 的顯示過(guò)程可以簡(jiǎn)單概括為:應(yīng)用程序把經(jīng)過(guò) measure(測(cè)量)、layout(布局)、draw(繪制)后的 surface 緩存數(shù)據(jù),通過(guò) SurfaceFlinger 把數(shù)據(jù)渲染到顯示屏幕上,通過(guò) Android 的刷新機(jī)制來(lái)刷新數(shù)據(jù)。換言之,應(yīng)用層負(fù)責(zé)繪制,系統(tǒng)層負(fù)責(zé)渲染,通過(guò)進(jìn)程間通信把應(yīng)用層需要繪制的數(shù)據(jù)傳遞到系統(tǒng)層服務(wù),系統(tǒng)層通過(guò)刷新機(jī)制把數(shù)據(jù)更新到屏幕上。

以下是有關(guān)概念的解釋:

  • Surface,surface持有用于最終顯示在屏幕上的像素?cái)?shù)據(jù),一個(gè) window 對(duì)應(yīng)著一個(gè) Surface,Android 中 每個(gè) view 都會(huì)繪制到 Surface 上,其中包含了兩個(gè)(小于4.1版本)或者三個(gè)(4.1及以上版本)緩沖區(qū)中。
  • Window,window 實(shí)際上是 View 的直接管理者,每個(gè) window 擁有一個(gè) surface,它的具體實(shí)現(xiàn)是 PhoneWindow,WindowManager 是外界訪問 window 的入口。
  • Canvas ,Canvas 是 Surface 繪圖時(shí)返回的一個(gè)接口,并提供豐富的繪圖的 API,用來(lái)進(jìn)行實(shí)際的繪圖操作。
應(yīng)用層

在 Android 中每個(gè) view 都會(huì)經(jīng)過(guò) measure 和 layout 來(lái)確定其所在的大小和位置,然后繪制到 surface (緩沖區(qū)上),繪制是由 ViewRootImpl 類中 performTraversals() 方法發(fā)起的。

Android支持兩種繪制方式:\color{red}{軟件繪制}\color{red}{硬件繪制}。硬件極速?gòu)?Android 3.0 開始支持,它在 UI 顯示和繪制效率方面遠(yuǎn)高于軟件繪制,但是它的也有缺點(diǎn):

  • 耗電,GPU 功耗高于 CPU。
  • 兼容性,不兼容某些接口和方法。
  • 內(nèi)存占用大。
系統(tǒng)層

經(jīng)過(guò)多次繪制后,要顯示的 view 相關(guān)的數(shù)據(jù)存儲(chǔ)(如大小和位置)在 Surface 的緩沖區(qū)中,接下來(lái)渲染操作交由系統(tǒng)進(jìn)程中的 SurfaceFlinger 服務(wù)來(lái)完成,這是一個(gè) IPC(進(jìn)程間通信)過(guò)程。SurfaceFlinger 的主要工作流程如下:

  • 響應(yīng)客戶端事件,創(chuàng)建 Layer 與客戶端的 Surface 建立連接。
  • 接收客戶端數(shù)據(jù)和屬性,修改 Layer 屬性,如尺寸、顏色、透明度等。
  • 將創(chuàng)建的 Layer 內(nèi)容刷新到屏幕上。
  • 維持 Layer 的序列,并對(duì) Layer 最終輸出做出裁剪計(jì)算。

當(dāng) Android 應(yīng)用層在圖形緩沖區(qū)中繪制好 View 層次結(jié)后,應(yīng)用層通過(guò) Binder 機(jī)制與 SurfaceFlinger 通信并借助一塊匿名共享內(nèi)存把圖形緩沖區(qū)交給 SurfaceFlinger 服務(wù)。由于單純的匿名共享服務(wù)在傳遞多個(gè)窗口數(shù)據(jù)時(shí)缺乏有效的管理,所以匿名共享內(nèi)存就被抽象為一個(gè)更上層的數(shù)據(jù)結(jié)構(gòu)——SharedClient,在 SharedClient 中,最多有 31 個(gè) SharedBufferStack,每個(gè) SharedBufferStack 都對(duì)應(yīng)一個(gè) Surface 即一個(gè) Window。這表明一個(gè) Android 應(yīng)用程序最多可以包含 31 個(gè) window。

繪制的過(guò)程首先是 CPU 準(zhǔn)備數(shù)據(jù)(measure、layout等),GPU 負(fù)責(zé)柵格化、渲染。因?yàn)閳D像 API 不允許 CPU 直接與 GPU 通信,所以要通過(guò)一個(gè)圖形驅(qū)動(dòng)的中間層來(lái)進(jìn)行連接。圖形驅(qū)動(dòng)里面維護(hù)了一個(gè)隊(duì)列,CPU 把 display list(待顯示的數(shù)據(jù)列表)添加到隊(duì)列中,GPU 從這個(gè)隊(duì)列中取出數(shù)據(jù)進(jìn)行繪制,最終在屏幕上顯示出來(lái),如下圖所示:


Android 系統(tǒng)每隔 16ms 會(huì)發(fā)出 VSYNC 信號(hào),觸發(fā)對(duì) UI 進(jìn)行渲染,如果每次都渲染成功,就能夠達(dá)到流暢畫面所需的 60PS。

垂直同步、雙緩沖和三級(jí)緩沖

雙緩沖

雙緩沖顧名思義是有兩個(gè)緩沖區(qū)(上文提到的 SharedBufferStack),分別是 FontBuffer(又叫作 FrameBuffer) 和 BackBuffer。UI 總是先在 Back Buffer 中繪制,然后再和 Font Buffer 交換,渲染到顯示設(shè)備中,即只有當(dāng)另一個(gè) buffer 的數(shù)據(jù)準(zhǔn)備好后,才會(huì)通過(guò)系統(tǒng)調(diào)用來(lái)通知顯示設(shè)備切換 Buffer。

雙緩沖機(jī)制在大部分情況下是適用的,但是如果某個(gè)環(huán)節(jié)出現(xiàn)了問題,CPU 資源就有可能存在浪費(fèi),如下圖所示:

VSYNC 類似與時(shí)鐘中斷。豎線分割的部分代表 16ms 的時(shí)間段。正常情況下,在每一時(shí)間段內(nèi),Display 顯示一幀數(shù)據(jù)(即每秒60幀)。

上圖中在第二個(gè) 16ms 時(shí)間段內(nèi),Display 本應(yīng)顯示 B 幀,但是因?yàn)?GPU 還在處理 B 幀,導(dǎo)致 A 幀被重復(fù)顯示。與此同時(shí),在第二個(gè)時(shí)間段內(nèi),處于 CPU 處于空閑狀態(tài),造成了浪費(fèi)。因?yàn)?A Buffer 被 Diaplay 在使用(SufaceFlinger 用完后不會(huì)釋放當(dāng)前的 Buffer,只會(huì)釋放舊的 Buffer),B Buffer 被 GPU 在使用,這就是 雙緩沖機(jī)制的局限性。

三級(jí)緩沖

Android 4.1 版本中對(duì) Android Display 系統(tǒng)進(jìn)行了重構(gòu),引入了三個(gè)核心元素:

  • \color{red}{VSYNC(垂直同步)} 。VSYNC 是 Vertical Synchronization 的縮寫,可以認(rèn)為是一種定時(shí)中斷。
  • \color{red}{Choreography(編舞者)} 。 Choreographer 起調(diào)度的作用,將繪制工作統(tǒng)一到 VSYNC 的某個(gè)時(shí)間點(diǎn)上,使應(yīng)用的繪制工作有序,具體來(lái)說(shuō)是當(dāng)收到 VSYNC 信號(hào)時(shí),調(diào)用用戶設(shè)置的回調(diào)函數(shù),回調(diào)類型的優(yōu)先級(jí)從高到低為 ALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL。
  • \color{red}{TripleBuffer} 。Triple Buffer 的引入是為了解決上文提到的 CPU 沒有 buffer 可用的窘境。如下圖所示:

在第二個(gè) 16ms 時(shí)間內(nèi),CPU 使用 C Buffer 繪圖,雖然還是會(huì)多顯示 A 幀一次,但是后續(xù)的顯示相對(duì)雙緩沖機(jī)制就順滑多了。但是 Buffer 并不是越多越好,從上圖可知,在第二個(gè)時(shí)間內(nèi),CPU 繪制的第 C 幀數(shù)據(jù)要到第四個(gè) 16ms 才能顯示,這比雙 Buffer 多了 16ms 的延遲。由此可見,雙緩沖保證低時(shí)延,三緩沖保證穩(wěn)定性。

整個(gè)流程簡(jiǎn)單來(lái)說(shuō)就是 CPU/GPU 會(huì)接收到 VSYNC 信號(hào),觸發(fā)對(duì) UI 進(jìn)行渲染(每 16ms 顯示一幀)。在 16ms 內(nèi)需要完成兩項(xiàng)任務(wù):將 UI 對(duì)象轉(zhuǎn)換為一系列多邊形和紋理(柵格化)和 CPU 傳遞處理數(shù)據(jù)到 GPU,更詳細(xì)的內(nèi)容可以看這篇文章Android的16ms和垂直同步以及三重緩存。

拓展

了解 Android 繪制流程后,我們不難反推 Android 應(yīng)用程序卡頓的原因:

  • 繪制任務(wù)太重,繪制一幀的時(shí)間耗時(shí)太長(zhǎng),超過(guò)了 16ms。常見的場(chǎng)景是布局層級(jí)嵌套過(guò)多、過(guò)度繪制等。
  • 主線程阻塞,導(dǎo)致 VSYNC 信號(hào)到來(lái)時(shí)還沒有準(zhǔn)備好數(shù)據(jù)導(dǎo)致丟幀。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容