UI 優(yōu)化系列專題占贫,來(lái)聊一聊 Android 渲染相關(guān)知識(shí),主要涉及 UI 渲染背景知識(shí)齐婴、如何優(yōu)化 UI 渲染兩部分內(nèi)容单匣。
UI 優(yōu)化系列專題
- UI 渲染背景知識(shí)
《View 繪制流程之 setContentView() 到底做了什么?》
《View 繪制流程之 DecorView 添加至窗口的過(guò)程》
《深入 Activity 三部曲(3)View 繪制流程》
《Android 之 LayoutInflater 全面解析》
《關(guān)于渲染铃绒,你需要了解什么鸽照?》
《Android 之 Choreographer 詳細(xì)分析》
- 如何優(yōu)化 UI 渲染
《Android 之如何優(yōu)化 UI 渲染(上)》
《Android 之如何優(yōu)化 UI 渲染(下)》
優(yōu)化是無(wú)止境的,Google 在 2012 年的 I/O 大會(huì)上宣布了 Project Butter 黃油計(jì)劃颠悬,并且在 Android 4.1 中正式開啟了這個(gè)機(jī)制矮燎。
Project Butter 主要包含兩個(gè)組成部分:一個(gè)是 VSYNC,另一個(gè)是 Triple Buffering赔癌。
也正是從這時(shí)候開始诞外,作為嚴(yán)重影響 Android 口碑的 UI 流暢性問(wèn)題便得到了有效解決。今天我們就來(lái)聊一聊這兩個(gè)組成部分的工作原理灾票,不過(guò)在理解它們之前峡谊,需要先看下所涉及的另外兩個(gè)概念:
1. 刷新率
表示屏幕在一秒內(nèi)刷新畫面的次數(shù), 刷新率取決于硬件的固定參數(shù),單位 HZ(Hz)靖苇。例如常見(jiàn)的 60 Hz席噩,即每秒鐘刷新 60 次。
逐行掃描
顯示器并不是一次性將畫面顯示到屏幕上贤壁,而是從左到右邊悼枢,從上到下逐行掃描顯示,不過(guò)這一過(guò)程快到人眼無(wú)法察覺(jué)到變化脾拆,以 60 Hz 刷新率的屏幕為例馒索,即 1000 / 60 ≈ 16ms。
2. 幀速率
表示 GPU 在一秒內(nèi)繪制操作的幀數(shù)名船。例如電影采用 24 fps绰上、Android 系統(tǒng)采用 60 fps,即一秒鐘繪制 30 / 60 幀畫面渠驼。更多內(nèi)容參考《Why 60 fps》蜈块。
屏幕撕裂
現(xiàn)在,刷新頻率和幀速率需要一起合作迷扇,才能使圖形內(nèi)容呈現(xiàn)在屏幕上百揭,GPU 會(huì)獲取圖形數(shù)據(jù)進(jìn)行繪制, 然后硬件負(fù)責(zé)把圖像內(nèi)容呈現(xiàn)到屏幕上蜓席,這一過(guò)程在應(yīng)用程序的生命周期內(nèi)一遍又一遍的發(fā)生器一。
不幸的是,刷新頻率和幀率并不總是能夠保持相對(duì)同步厨内,如果你的幀速率實(shí)際比刷新率快祈秕,例如幀速率是 120 fps,顯示器的刷新頻率為 60 Hz雏胃。此時(shí)將會(huì)發(fā)生一些視覺(jué)上的問(wèn)題请毛。
由于顯示器提取畫面是從左到右,從上到下逐行掃描提取圖像顯示丑掺,這一過(guò)程需要 16ms(1000 / 60)获印,當(dāng) GPU 利用一塊內(nèi)存區(qū)域?qū)懭霂瑪?shù)據(jù)時(shí),從頂部開始新一幀覆蓋前一幀街州,并立刻輸出一行內(nèi)容兼丰。當(dāng)屏幕刷新時(shí),它并不知道圖像緩沖區(qū)的狀態(tài)唆缴,因此它從 GPU 抓取的幀并不是完整的數(shù)據(jù)鳍征。也就是它有一半的前一幀和一半的當(dāng)前幀,這種情況被稱之為屏幕撕裂面徽。
- 理想情況下艳丛,希望顯示器在完成一幀繪制之后再?gòu)?GPU 獲取到下一幀圖像數(shù)據(jù)匣掸,但是由于幀率和刷新頻率不一致導(dǎo)致顯示器在繪圖過(guò)程中,GPU 已經(jīng)開始加載下一幀圖像數(shù)據(jù)氮双。屏幕撕裂是由于幀率和刷新頻率不一致的情況導(dǎo)致碰酝。
解決該問(wèn)題的方案是雙緩沖,即 GPU 和顯示器都有各自的工作緩沖區(qū)戴差。GPU 始終將完成的一幀繪制數(shù)據(jù)寫入到 Back Buffer送爸,而顯示器使用 Frame Buffer。當(dāng)屏幕刷新時(shí)暖释,F(xiàn)rame Buffer 并不會(huì)發(fā)生變化袭厂。Back Buffer 根據(jù)屏幕的刷新將數(shù)據(jù) copy 到 Frame Buffer,這便是 VSYNC 的用武之地球匕。
在 Android 4.1 之前纹磺,Android 使用雙緩沖機(jī)制。怎么理解呢亮曹?一般來(lái)說(shuō)橄杨,同一個(gè) View Hierarchy 內(nèi)的 View 都會(huì)共用一個(gè) Window,也就是共用一個(gè) Surface照卦。
而每個(gè) Surface 都會(huì)有一個(gè) BufferQueue 緩存隊(duì)列讥珍,但是這個(gè)隊(duì)列會(huì)由 SurfaceFlinger 管理,通過(guò)匿名共享內(nèi)存與 App 應(yīng)用層交互窄瘟。
整個(gè)流程如下:
每個(gè) Surface 對(duì)應(yīng)的 BufferQueue 內(nèi)部都有兩個(gè) Graphic Buffer,一個(gè)用于繪制一個(gè)用于顯示趟卸。系統(tǒng)會(huì)把內(nèi)容先到離屏緩沖區(qū)(OffScreen Buffer)蹄葱,在需要顯示時(shí),才把離屏緩沖區(qū)的內(nèi)容通過(guò) Swap Buffer 復(fù)制到 Front Graphic Buffer 中锄列。
這樣 SurfaceFlinger 就拿到了某個(gè) Surface 最終要顯示的內(nèi)容图云,但是同一時(shí)間我們可能會(huì)有多個(gè) Surface。這里面可能是不同應(yīng)用的 Surface邻邮,也可能是同一個(gè)應(yīng)用里面類似 SurfaceView 和 TextureView竣况,它們都會(huì)有自己?jiǎn)为?dú)的 Surface。
這個(gè)時(shí)候 SurfaceFlinger 把所有 Surface 要顯示的內(nèi)容統(tǒng)一交給 Hardware Composer筒严,它會(huì)根據(jù)位置丹泉、Z-Order 順序等信息合成為最終屏幕需要顯示的內(nèi)容,而這個(gè)內(nèi)容會(huì)交給系統(tǒng)的幀緩沖區(qū) Frame Buffer 來(lái)顯示(Frame Buffer 是非常底層的鸭蛙,可以理解為屏幕顯示的抽象)摹恨。
Android 一直使用 VSYNC 來(lái)阻止屏幕撕裂,對(duì)于 Android 4.0娶视,CPU 可能會(huì)因?yàn)樵诿ζ渌氖虑樯购澹瑢?dǎo)致沒(méi)來(lái)得及處理 UI 繪制睁宰。所以從 4.1 開始 VSYNC 則更進(jìn)一步,VSYNC 脈沖現(xiàn)在用于開始下一幀的所有處理寝凌。
VSYNC 類似于時(shí)鐘中斷柒傻,每收到 VSYNC 中斷,CPU 會(huì)立即準(zhǔn)備 Buffer 數(shù)據(jù)较木,由于大部分顯示設(shè)備刷新頻率都是 60Hz(一秒刷新 60 次)红符,也就是說(shuō)一幀數(shù)據(jù)的準(zhǔn)備工作都要在 16ms(1000/60≈16)內(nèi)完成。
這樣應(yīng)用總是在 VSYNC 邊界上開始繪制劫映,而 SurfaceFlinger 總是在 VSYNC 邊界上進(jìn)行合成违孝。這樣便可以消除卡頓,并提升圖形的視覺(jué)表現(xiàn)泳赋。
Triple Buffering
如果理解了雙緩沖機(jī)制的原理雌桑,那就非常容易理解什么是三緩沖區(qū)了。如果只有兩個(gè) Graphic Buffer 緩存區(qū) A 和 B祖今,如果 CPU/GPU 繪制過(guò)程較長(zhǎng)校坑,超過(guò)了一個(gè) VSYNC 信號(hào)周期,因?yàn)榫彌_區(qū) B 中的數(shù)據(jù)還沒(méi)有準(zhǔn)備完成千诬,所以只能繼續(xù)展示 A 緩沖區(qū)的內(nèi)容耍目,這樣緩沖區(qū) A 和 B 都分別被顯示設(shè)備和 GPU 占用,CPU 則無(wú)法準(zhǔn)備下一幀的數(shù)據(jù)徐绑。
如果再提供一個(gè)緩沖區(qū)邪驮,CPU、GPU 和顯示設(shè)備都能使用各自的緩沖區(qū)工作毅访,互不影響盘榨。簡(jiǎn)單來(lái)說(shuō),三緩沖機(jī)制就是在雙緩沖機(jī)制基礎(chǔ)上增加了一個(gè) Graphic Buffer 緩沖區(qū)草巡,這樣可以最大限度利用空閑時(shí)間守呜,帶來(lái)的壞處是多使用了一個(gè) Graphic Buffer 所占用的內(nèi)存查乒。
從圖中可以看出,緩沖區(qū) B 花費(fèi)的時(shí)間太長(zhǎng)萍歉,并且正在使用 A 來(lái)顯示當(dāng)前幀。不過(guò)這次憔晒,系統(tǒng)不是在重復(fù)的緩沖區(qū)中浪費(fèi)時(shí)間拒担,而是創(chuàng)建一個(gè) C 緩沖區(qū),并開始處理下一幀州弟。三重緩沖降低了 jank 的進(jìn)一步加劇婆翔。
三緩沖并不總是存在掏婶,通常情況下僅運(yùn)行一個(gè)雙緩沖區(qū)雄妥,但是當(dāng)發(fā)生延遲等情況時(shí)老厌,第三個(gè)緩沖區(qū)就會(huì)出現(xiàn)枝秤,以便降低延遲的加劇。對(duì)于 VSYNC 信號(hào)和 Triple Buffering 更詳細(xì)的介紹趁仙,可以參考《Project Butter - How it works and What it added?》干奢。
Android 渲染框架非常龐大忿峻,而且演進(jìn)的非常快搓谆。感興趣的朋友可以進(jìn)一步閱讀下面的參考資料到逊。
- Understanding VSYNC
- 2018 Google I/O:Drawn out: how Android renders
- Google I/O 2012:For Butter or Worse
- 官方文檔:Android 圖形架構(gòu)
- Android Project Butter 分析