關(guān)于 Android 渲染你應(yīng)該了解的知識(shí)點(diǎn)
前言
談到Android的UI繪制,大家可能會(huì)想到onMeasure、onLayout、onDraw三大流程。但我們的View到底是如何一步一步顯示到屏幕上的?onDraw之后到View顯示到屏幕上,具體又做了哪些工作?
帶著這些問(wèn)題,我們今天就深入學(xué)習(xí)一下Android渲染的流程吧,本文主包括以下內(nèi)容:
Android渲染的整體架構(gòu)是怎樣的?Android渲染的生產(chǎn)者包括哪些?Skia與OpenGl的區(qū)別是什么?什么是硬件加速?硬件繪制與軟件繪制的區(qū)別 Android渲染緩沖區(qū)是什么?什么是黃油計(jì)劃?Android渲染的消費(fèi)者是什么? 什么是SurfaceFlinger?
Android渲染整體架構(gòu)

我們先來(lái)看一下Android渲染的整體架構(gòu),具體可分為以下幾個(gè)部分
image stream produceers: ?渲染數(shù)據(jù)的生產(chǎn)者,如App的draw方法會(huì)把繪制指令通過(guò)canvas傳遞給framework層的RenderThread線程。native Framework:RenderThread線程通過(guò)surface.dequeue得到緩沖區(qū)graphic bufer,然后在上面通過(guò)OpenGL來(lái)完成真正的渲染命令。在把緩沖區(qū)交還給BufferQueue隊(duì)列中。image stream consumers:surfaceFlinger從隊(duì)列中獲取數(shù)據(jù),同時(shí)和HAL完成layer的合成工作,最終交給HAL展示。HAL: ?硬件抽象層。把圖形數(shù)據(jù)展示到設(shè)備屏幕
可以看出,這其實(shí)是個(gè)很典型的生產(chǎn)者消費(fèi)者模式
圖像生產(chǎn)者:也就是我們的 APP,再深入點(diǎn)就是canvas->surface。圖像消費(fèi)者: SurfaceFlinger圖像緩沖區(qū): BufferQueue,一般是3緩沖區(qū)
下面我們就從生產(chǎn)者,消費(fèi)者,緩沖區(qū)三個(gè)部分來(lái)詳細(xì)了解下Android渲染的過(guò)程
圖像生產(chǎn)者
從上面的架構(gòu)圖可知,圖像的生產(chǎn)者主要有MediaPlayer,CameraPreview,NDK(Skia),OpenGl ES。
其中MediaPlayer和Camera Preview是通過(guò)直接讀取圖像源來(lái)生成圖像數(shù)據(jù),NDK(Skia),OpenGL ES是通過(guò)自身的繪制能力生產(chǎn)的圖像數(shù)據(jù)
OpenGL、Vulkan、Skia的區(qū)別
OpenGL:是一種跨平臺(tái)的3D圖形繪制規(guī)范接口。OpenGL EL則是專門(mén)針對(duì)嵌入式設(shè)備,如手機(jī)做了優(yōu)化。Skia:skia是圖像渲染庫(kù),2D圖形繪制自己就能完成。3D效果(依賴硬件)由OpenGL、Vulkan、Metal支持。它不僅支持2D、3D,同時(shí)支持CPU軟件繪制和GPU硬件加速。Android、flutter都是使用它來(lái)完成繪制。Vulkan:Android引入了Vulkan支持。VulKan是用來(lái)替換OpenGL的。它不僅支持3D,也支持2D,同時(shí)更加輕量級(jí)
硬件加速
關(guān)于硬件加速,相信大家也經(jīng)常聽(tīng)到,尤其是有些API不支持硬件加速,因此需要我們手動(dòng)關(guān)閉,那么硬件加速到底是什么呢?
CPU 與 GPU的區(qū)別
除了屏幕,UI 渲染還要依賴另外兩個(gè)核心的硬件:CPU 和 GPU。
CPU(Central Processing Unit,中央處理器),是計(jì)算機(jī)系統(tǒng)的運(yùn)算和控制核心,是信息處理、程序運(yùn)行的最終執(zhí)行單元;GPU(Graphics Processin Unit,圖形處理器),是一種專門(mén)用于圖像運(yùn)算的處理器,在計(jì)算機(jī)系統(tǒng)中通常被稱為 "顯卡"的核心部件就是GPU。
UI 組件在繪制到屏幕之前,都需要經(jīng)過(guò) Rasterization(柵格化)操作,而柵格化又是一個(gè)非常耗時(shí)的操作。
Rasterization 柵格化是繪制那些 Button、Shape、Path、String、Bitmap 等顯示組件最基礎(chǔ)的操作。柵格化將這些 UI 組件拆分到顯示器的不同像素上進(jìn)行顯示。這是一個(gè)非常耗時(shí)的操作,GPU 的引入就是為了加快柵格化。
硬件繪制與軟件繪制

從圖中可以看到,軟件繪制使用 Skia庫(kù),它是一款能在低端設(shè)備,如手機(jī)呈現(xiàn)高質(zhì)量的2D跨平臺(tái)圖形框架,類似Chrome、Flutter內(nèi)部使用的都是Skia庫(kù)。硬件繪制的思想就是通過(guò)底層軟件代碼,將 CPU不擅長(zhǎng)的圖形計(jì)算轉(zhuǎn)換成GPU專用指令,由GPU完成繪制任務(wù)。
所以說(shuō)硬件加速的本質(zhì)就是使用GPU代替CPU完成Graphic Buffer繪制工作,以實(shí)現(xiàn)更好的性能,Android從4.0開(kāi)始默認(rèn)開(kāi)啟了硬件加速,但還有一些API不支持硬件加速,因此需要手動(dòng)關(guān)閉硬件加速。
需要注意的是,軟件繪制使用的Skia庫(kù),但這不代表Skia不支持硬件加速,從Android 8開(kāi)始,我們可以選擇使用Skia進(jìn)行硬件加速,Android 9開(kāi)始就默認(rèn)使用Skia來(lái)進(jìn)行硬件加速。Skia的硬件加速主要是通過(guò) copybit 模塊調(diào)用OpenGL或者SKia來(lái)實(shí)現(xiàn)。
圖像緩沖區(qū)
Android中的圖像生產(chǎn)者OpenGL,Skia,Vulkan將繪制的數(shù)據(jù)存放在圖像緩沖區(qū)中,Android中的圖像消費(fèi)SurfaceFlinger從圖像緩沖區(qū)將數(shù)據(jù)取出,進(jìn)行加工及合成
那么圖像緩沖區(qū)我們又需要注意哪些內(nèi)容呢?
黃油計(jì)劃
優(yōu)化是無(wú)止境的,Google 在 2012 年的 I/O 大會(huì)上宣布了 Project Butter 黃油計(jì)劃,并且在 Android 4.1 中正式開(kāi)啟了這個(gè)機(jī)制。
VSYNC信號(hào)
VSYNC(Vertical Synchronization)是理解 Project Butter 的核心。對(duì)于 Android 4.0,CPU 可能會(huì)因?yàn)樵诿ζ渌氖虑?,?dǎo)致沒(méi)來(lái)得及處理 UI 繪制。
為了解決這個(gè)問(wèn)題,系統(tǒng)在收到VSync信號(hào)后,將馬上開(kāi)始下一幀的渲染。即一旦收到VSync通知(16ms觸發(fā)一次),CPU和GPU 才立刻開(kāi)始計(jì)算然后把數(shù)據(jù)寫(xiě)入buffer。如下圖
CPU/GPU根據(jù)VSYNC信號(hào)同步處理數(shù)據(jù),可以讓CPU/GPU有完整的16ms時(shí)間來(lái)處理數(shù)據(jù),減少了jank。
一句話總結(jié),VSync同步使得CPU/GPU充分利用了16.6ms時(shí)間,減少jank。
三緩沖機(jī)制
在Android 4.0之前,Android采用雙緩沖機(jī)制,讓繪制和顯示器擁有各自的buffer:GPU 始終將完成的一幀圖像數(shù)據(jù)寫(xiě)入到 Back Buffer,而顯示器使用 Frame Buffer,當(dāng)屏幕刷新時(shí),Frame Buffer 并不會(huì)發(fā)生變化,當(dāng)Back buffer準(zhǔn)備就緒后,它們才進(jìn)行交換。
但是如果界面比較復(fù)雜,CPU/GPU的處理時(shí)間較長(zhǎng) 超過(guò)了16.6ms呢,雙緩沖機(jī)制會(huì)帶來(lái)什么問(wèn)題?如下圖:
在第二個(gè)時(shí)間段內(nèi),但卻因 GPU還在處理B幀,緩存沒(méi)能交換,導(dǎo)致A幀被重復(fù)顯示。而 B完成后,又因?yàn)槿狈?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">VSync信號(hào),它只能等待下一個(gè)signal的來(lái)臨。于是在這一過(guò)程中,有一大段時(shí)間是被浪費(fèi)的。當(dāng)下一個(gè) VSync出現(xiàn)時(shí),CPU/GPU馬上執(zhí)行操作(A幀),且緩存交換,相應(yīng)的顯示屏對(duì)應(yīng)的就是B。這時(shí)看起來(lái)就是正常的。只不過(guò)由于執(zhí)行時(shí)間仍然超過(guò)16ms,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復(fù),便出現(xiàn)了越來(lái)越多的“Jank”。
三緩沖就是在雙緩沖機(jī)制基礎(chǔ)上增加了一個(gè)Graphic Buffer緩沖區(qū),這樣可以最大限度的利用空閑時(shí)間,帶來(lái)的壞處是多使用的一個(gè)Graphic Buffer所占用的內(nèi)存。
三緩沖機(jī)制有效利用了等待vysnc的時(shí)間,可以幫助我們減少了jank
RenderThread
經(jīng)過(guò) Android 4.1 的 Project Butter 黃油計(jì)劃之后,Android 的渲染性能有了很大的改善。不過(guò)你有沒(méi)有注意到這樣一個(gè)問(wèn)題,雖然利用了 GPU 的圖形高性能運(yùn)算,但是從計(jì)算 DisplayList,到通過(guò) GPU 繪制到 Frame Buffer,整個(gè)計(jì)算和繪制都在 UI 主線程中完成。
UI 線程任務(wù)過(guò)于繁重。如果整個(gè)渲染過(guò)程比較耗時(shí),可能造成無(wú)法響應(yīng)用戶的操作,進(jìn)而出現(xiàn)卡頓的情況。GPU 對(duì)圖形的繪制渲染能力更勝一籌,如果使用 GPU 并在不同線程繪制渲染圖形,那么整個(gè)流程會(huì)更加順暢。
正因如此,在 Android 5.0 引入兩個(gè)比較大的改變。一個(gè)是引入了 RenderNode 的概念,它對(duì) DisplayList 及一些 View 顯示屬性都做了進(jìn)一步封裝。另一個(gè)是引入了 RenderThread,所有的 GL 命令執(zhí)行都放到這個(gè)線程上,渲染線程在 RenderNode 中存有渲染幀的所有信息,可以做一些屬性動(dòng)畫(huà),這樣即便主線程有耗時(shí)操作的時(shí)候也可以保證動(dòng)畫(huà)流程。
圖像消費(fèi)者
SurfaceFlinger是Android系統(tǒng)中最重要的一個(gè)圖像消費(fèi)者,Activity繪制的界面圖像,都會(huì)傳遞到SurfaceFlinger來(lái),SurfaceFlinger的作用主要是接收GraphicBuffer,然后交給HWComposer或者OpenGL做合成,合成完成后,SurfaceFlinger會(huì)把最終的數(shù)據(jù)提交給FrameBuffer。
SurfaceFlinger是圖像數(shù)據(jù)的消費(fèi)者。在應(yīng)用程序請(qǐng)求創(chuàng)建surface的時(shí)候,SurfaceFlinger會(huì)創(chuàng)建一個(gè)Layer。Layer是SurfaceFlinger操作合成的基本單元。所以,一個(gè)surface對(duì)應(yīng)一個(gè)Layer。
當(dāng)應(yīng)用程序把繪制好的GraphicBuffer數(shù)據(jù)放入BufferQueue后,接下來(lái)的工作就是SurfaceFlinger來(lái)完成了。
系統(tǒng)會(huì)有多個(gè)應(yīng)用程序,一個(gè)程序有多個(gè)BufferQueue隊(duì)列。SurfaceFlinger就是用來(lái)決定何時(shí)以及怎么去管理和顯示這些隊(duì)列的。SurfaceFlinger請(qǐng)求HAL硬件層,來(lái)決定這些Buffer是硬件來(lái)合成還是自己通過(guò)OpenGL來(lái)合成。
最終把合成后的buffer數(shù)據(jù),展示在屏幕上。
總結(jié)
總得來(lái)說(shuō),Android圖像渲染機(jī)制是一個(gè)生產(chǎn)者消費(fèi)者的模型,如下圖所示:
onMeasure、onLayout計(jì)算出view的大小和擺放的位置,這都是UI線程要做的事情,在draw方法中進(jìn)行繪制,但此時(shí)是沒(méi)有真正去繪制。而是把繪制的指令封裝為displayList,進(jìn)一步封裝為RenderNode,在同步給RenderThread。RenderThread通過(guò)dequeue拿到graphic buffer(surfaceFlinger的緩沖區(qū)),根據(jù)繪制指令直接操作OpenGL的繪制接口,最終通過(guò)GPU設(shè)備把繪制指令渲染到了離屏緩沖區(qū)graphic buffer。完成渲染后,把緩沖區(qū)交還給 SurfaceFlinger的BufferQueue。SurfaceFlinger會(huì)通過(guò)硬件設(shè)備進(jìn)行layer的合成,最終展示到屏幕。
技術(shù)問(wèn)答,學(xué)習(xí)成長(zhǎng),歡迎加入音視頻開(kāi)發(fā)進(jìn)階知識(shí)星球

技術(shù)交流,歡迎加我微信:ezglumes ,拉你入技術(shù)交流群。
私信領(lǐng)取相關(guān)資料
推薦閱讀:
音視頻開(kāi)發(fā)工作經(jīng)驗(yàn)分享 || 視頻版
開(kāi)通專輯 | 細(xì)數(shù)那些年寫(xiě)過(guò)的技術(shù)文章專輯
Android NDK 免費(fèi)視頻在線學(xué)習(xí)!??!
你想要的音視頻開(kāi)發(fā)資料庫(kù)來(lái)了
推薦幾個(gè)堪稱教科書(shū)級(jí)別的 Android 音視頻入門(mén)項(xiàng)目
覺(jué)得不錯(cuò),點(diǎn)個(gè)在看唄~

