前言
談到Android
的UI
繪制童社,大家可能會想到onMeasure
、onLayout
斗埂、onDraw
三大流程符糊。但我們的View
到底是如何一步一步顯示到屏幕上的?onDraw
之后到View
顯示到屏幕上呛凶,具體又做了哪些工作? 帶著這些問題男娄,我們今天就深入學習一下Android
渲染的流程吧,本文主包括以下內(nèi)容:
Android
渲染的整體架構(gòu)是怎樣的?Android
渲染的生產(chǎn)者包括哪些漾稀?Skia
與OpenGl
的區(qū)別是什么模闲?什么是硬件加速?硬件繪制與軟件繪制的區(qū)別
Android
渲染緩沖區(qū)是什么崭捍?什么是黃油計劃?Android
渲染的消費者是什么? 什么是SurfaceFlinger
?
Android
渲染整體架構(gòu)
Android
渲染的整體架構(gòu)尸折,具體可分為以下幾個部分
image stream produceers
: 渲染數(shù)據(jù)的生產(chǎn)者,如App
的draw
方法會把繪制指令通過canvas
傳遞給framework
層的RenderThread
線程殷蛇。native Framework
:RenderThread
線程通過surface.dequeue
得到緩沖區(qū)graphic bufer
实夹,然后在上面通過OpenGL
來完成真正的渲染命令。在把緩沖區(qū)交還給BufferQueue
隊列中粒梦。image stream consumers
:surfaceFlinger
從隊列中獲取數(shù)據(jù)亮航,同時和HAL
完成layer
的合成工作,最終交給HAL
展示匀们。HAL
: 硬件抽象層缴淋。把圖形數(shù)據(jù)展示到設(shè)備屏幕
圖像生產(chǎn)者: 也就是我們的
APP
泄朴,再深入點就是canvas->surface
重抖。圖像消費者:
SurfaceFlinger
圖像緩沖區(qū):
BufferQueue
,一般是3緩沖區(qū)
下面我們就從生產(chǎn)者祖灰,消費者钟沛,緩沖區(qū)三個部分來詳細了解下Android
渲染的過程
圖像生產(chǎn)者
從上面的架構(gòu)圖可知,圖像的生產(chǎn)者主要有MediaPlayer
局扶,CameraPreview
恨统,NDK(Skia)
,OpenGl ES
详民。 其中MediaPlayer
和Camera Preview
是通過直接讀取圖像源來生成圖像數(shù)據(jù)延欠,NDK(Skia)
陌兑,OpenGL ES
是通過自身的繪制能力生產(chǎn)的圖像數(shù)據(jù)
OpenGL
沈跨、Vulkan
、Skia
的區(qū)別
OpenGL
: 是一種跨平臺的3D
圖形繪制規(guī)范接口兔综。OpenGL EL
則是專門針對嵌入式設(shè)備饿凛,如手機做了優(yōu)化狞玛。Skia
:skia
是圖像渲染庫,2D
圖形繪制自己就能完成涧窒。3D
效果(依賴硬件)由OpenGL
心肪、Vulkan
、Metal
支持纠吴。它不僅支持2D
硬鞍、3D
,同時支持CPU
軟件繪制和GPU
硬件加速戴已。Android
固该、flutter
都是使用它來完成繪制。Vulkan
:Android
引入了Vulkan
支持糖儡。VulKan
是用來替換OpenGL
的伐坏。它不僅支持3D
,也支持2D
握联,同時更加輕量級
硬件加速
關(guān)于硬件加速桦沉,相信大家也經(jīng)常聽到,尤其是有些API
不支持硬件加速金闽,因此需要我們手動關(guān)閉纯露,那么硬件加速到底是什么呢?
CPU
與 GPU
的區(qū)別
除了屏幕,UI
渲染還要依賴另外兩個核心的硬件:CPU
和 GPU
呐矾。
CPU
(Central Processing Unit
苔埋,中央處理器),是計算機系統(tǒng)的運算和控制核心蜒犯,是信息處理组橄、程序運行的最終執(zhí)行單元;GPU
(Graphics Processin Unit
罚随,圖形處理器)玉工,是一種專門用于圖像運算的處理器,在計算機系統(tǒng)中通常被稱為 "顯卡"的核心部件就是GPU
淘菩。
UI
組件在繪制到屏幕之前遵班,都需要經(jīng)過 Rasterization
(柵格化)操作,而柵格化又是一個非常耗時的操作潮改。 [圖片上傳失敗...(image-1273b8-1675927727899)] Rasterization
柵格化是繪制那些 Button
狭郑、Shape
、Path
汇在、String
翰萨、Bitmap
等顯示組件最基礎(chǔ)的操作。柵格化將這些 UI
組件拆分到顯示器的不同像素上進行顯示糕殉。這是一個非常耗時的操作亩鬼,GPU
的引入就是為了加快柵格化殖告。
硬件繪制與軟件繪制
,軟件繪制使用
Skia
庫雳锋,它是一款能在低端設(shè)備黄绩,如手機呈現(xiàn)高質(zhì)量的2D
跨平臺圖形框架,類似Chrome
玷过、Flutter
內(nèi)部使用的都是Skia
庫爽丹。硬件繪制的思想就是通過底層軟件代碼,將
CPU
不擅長的圖形計算轉(zhuǎn)換成GPU
專用指令辛蚊,由GPU
完成繪制任務习劫。
所以說硬件加速的本質(zhì)就是使用GPU
代替CPU
完成Graphic Buffer
繪制工作,以實現(xiàn)更好的性能嚼隘,Android
從4.0開始默認開啟了硬件加速诽里,但還有一些API
不支持硬件加速,因此需要手動關(guān)閉硬件加速飞蛹。 需要注意的是谤狡,軟件繪制使用的Skia
庫,但這不代表Skia
不支持硬件加速卧檐,從Android 8
開始墓懂,我們可以選擇使用Skia
進行硬件加速,Android 9
開始就默認使用Skia
來進行硬件加速霉囚。Skia
的硬件加速主要是通過 copybit
模塊調(diào)用OpenGL
或者SKia
來實現(xiàn)捕仔。
圖像緩沖區(qū)
Android
中的圖像生產(chǎn)者OpenGL
,Skia
盈罐,Vulkan
將繪制的數(shù)據(jù)存放在圖像緩沖區(qū)中榜跌,Android
中的圖像消費SurfaceFlinger
從圖像緩沖區(qū)將數(shù)據(jù)取出,進行加工及合成 那么圖像緩沖區(qū)我們又需要注意哪些內(nèi)容呢?
黃油計劃
優(yōu)化是無止境的盅粪,Google
在 2012 年的 I/O
大會上宣布了 Project Butter
黃油計劃钓葫,并且在 Android 4.1
中正式開啟了這個機制。
VSYNC
信號
VSYNC(Vertical Synchronization)
是理解 Project Butter
的核心票顾。對于 Android 4.0
础浮,CPU
可能會因為在忙其他的事情,導致沒來得及處理 UI
繪制奠骄。 為了解決這個問題豆同,系統(tǒng)在收到VSync
信號后,將馬上開始下一幀的渲染含鳞。即一旦收到VSync
通知(16ms
觸發(fā)一次)影锈,CPU
和GPU
才立刻開始計算然后把數(shù)據(jù)寫入buffer
。如下圖 [圖片上傳失敗...(image-eb415-1675927727899)]
CPU/GPU
根據(jù)VSYNC
信號同步處理數(shù)據(jù),可以讓CPU/GPU
有完整的16ms時間來處理數(shù)據(jù),減少了jank
。 一句話總結(jié)嘿般,VSync
同步使得CPU/GPU
充分利用了16.6ms時間魂仍,減少jank
。
三緩沖機制
在Android 4.0
之前磁滚,Android
采用雙緩沖機制佛吓,讓繪制和顯示器擁有各自的buffer
:GPU
始終將完成的一幀圖像數(shù)據(jù)寫入到 Back Buffer
,而顯示器使用 Frame Buffer
垂攘,當屏幕刷新時维雇,Frame Buffer
并不會發(fā)生變化,當Back buffer
準備就緒后晒他,它們才進行交換吱型。
但是如果界面比較復雜,CPU/GPU
的處理時間較長 超過了16.6ms呢陨仅,雙緩沖機制會帶來什么問題津滞?如下圖: [圖片上傳失敗...(image-75841b-1675927727899)]
在第二個時間段內(nèi),但卻因
GPU
還在處理B
幀灼伤,緩存沒能交換触徐,導致A
幀被重復顯示。而
B
完成后狐赡,又因為缺乏VSync
信號撞鹉,它只能等待下一個signal
的來臨。于是在這一過程中颖侄,有一大段時間是被浪費的鸟雏。當下一個
VSync
出現(xiàn)時,CPU/GPU
馬上執(zhí)行操作(A
幀)览祖,且緩存交換崔慧,相應的顯示屏對應的就是B
。這時看起來就是正常的穴墅。只不過由于執(zhí)行時間仍然超過16ms惶室,導致下一次應該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復,便出現(xiàn)了越來越多的“Jank”玄货。
三緩沖就是在雙緩沖機制基礎(chǔ)上增加了一個Graphic Buffer
緩沖區(qū)皇钞,這樣可以最大限度的利用空閑時間,帶來的壞處是多使用的一個Graphic Buffer
所占用的內(nèi)存松捉。 [圖片上傳失敗...(image-8c8ef2-1675927727899)] 三緩沖機制有效利用了等待vysnc的時間夹界,可以幫助我們減少了jank
RenderThread
經(jīng)過 Android 4.1
的 Project Butter
黃油計劃之后,Android
的渲染性能有了很大的改善隘世。不過你有沒有注意到這樣一個問題可柿,雖然利用了 GPU
的圖形高性能運算鸠踪,但是從計算 DisplayList
,到通過 GPU
繪制到 Frame Buffer
复斥,整個計算和繪制都在 UI
主線程中完成营密。
UI
線程任務過于繁重。如果整個渲染過程比較耗時目锭,可能造成無法響應用戶的操作评汰,進而出現(xiàn)卡頓的情況。GPU
對圖形的繪制渲染能力更勝一籌痢虹,如果使用 GPU
并在不同線程繪制渲染圖形被去,那么整個流程會更加順暢。
正因如此奖唯,在 Android 5.0
引入兩個比較大的改變惨缆。一個是引入了 RenderNode
的概念,它對 DisplayList
及一些 View
顯示屬性都做了進一步封裝丰捷。另一個是引入了 RenderThread
踪央,所有的 GL
命令執(zhí)行都放到這個線程上,渲染線程在 RenderNode
中存有渲染幀的所有信息瓢阴,可以做一些屬性動畫畅蹂,這樣即便主線程有耗時操作的時候也可以保證動畫流程。
圖像消費者
SurfaceFlinger
是Android
系統(tǒng)中最重要的一個圖像消費者荣恐,Activity
繪制的界面圖像液斜,都會傳遞到SurfaceFlinger
來,SurfaceFlinger
的作用主要是接收GraphicBuffer
叠穆,然后交給HWComposer
或者OpenGL
做合成少漆,合成完成后,SurfaceFlinger
會把最終的數(shù)據(jù)提交給FrameBuffer
硼被。
SurfaceFlinger
是圖像數(shù)據(jù)的消費者示损。在應用程序請求創(chuàng)建surface
的時候,SurfaceFlinger
會創(chuàng)建一個Layer
嚷硫。Layer
是SurfaceFlinger
操作合成的基本單元检访。所以,一個surface
對應一個Layer
仔掸。 當應用程序把繪制好的GraphicBuffer
數(shù)據(jù)放入BufferQueue
后脆贵,接下來的工作就是SurfaceFlinger
來完成了。
系統(tǒng)會有多個應用程序起暮,一個程序有多個BufferQueue
隊列卖氨。SurfaceFlinger
就是用來決定何時以及怎么去管理和顯示這些隊列的。 SurfaceFlinger
請求HAL
硬件層,來決定這些Buffer
是硬件來合成還是自己通過OpenGL
來合成筒捺。 最終把合成后的buffer
數(shù)據(jù)柏腻,展示在屏幕上。
總結(jié)
總得來說系吭,Android
圖像渲染機制是一個生產(chǎn)者消費者的模型五嫂,如下圖所示:
onMeasure
、onLayout
計算出view
的大小和擺放的位置村斟,這都是UI
線程要做的事情,在draw
方法中進行繪制抛猫,但此時是沒有真正去繪制蟆盹。而是把繪制的指令封裝為displayList
,進一步封裝為RenderNode
,在同步給RenderThread
闺金。RenderThread
通過dequeue
拿到graphic buffer
(surfaceFlinger
的緩沖區(qū))逾滥,根據(jù)繪制指令直接操作OpenGL
的繪制接口,最終通過GPU
設(shè)備把繪制指令渲染到了離屏緩沖區(qū)graphic buffer
败匹。完成渲染后寨昙,把緩沖區(qū)交還給
SurfaceFlinger
的BufferQueue
。SurfaceFlinger
會通過硬件設(shè)備進行layer
的合成掀亩,最終展示到屏幕舔哪。