Android 顯示原理簡(jiǎn)介

作者:yearzhu呀酸,2011年進(jìn)入騰訊公司隆箩,從事過(guò)Web端及移動(dòng)端的測(cè)試工作该贾,喜愛(ài)新鮮事物及新技術(shù),目前在SNG開(kāi)放平臺(tái)測(cè)試組負(fù)責(zé)的移動(dòng)互聯(lián)SDK的測(cè)試工作捌臊。

現(xiàn)在越來(lái)越多的應(yīng)用開(kāi)始重視流暢度方面的測(cè)試杨蛋,了解Android應(yīng)用程序是如何在屏幕上顯示的則是基礎(chǔ)中的基礎(chǔ),就讓我們一起看看小小屏幕中大大的學(xué)問(wèn)理澎。這也是我下篇文章——《Android應(yīng)用流暢度測(cè)試分析》的基礎(chǔ)逞力。

首先,用一句話(huà)來(lái)概括一下Android應(yīng)用程序顯示的過(guò)程:Android應(yīng)用程序調(diào)用SurfaceFlinger服務(wù)把經(jīng)過(guò)測(cè)量糠爬、布局和繪制后的Surface渲染到顯示屏幕上寇荧。

名詞解釋

SurfaceFlinger:Android系統(tǒng)服務(wù),負(fù)責(zé)管理Android系統(tǒng)的幀緩沖區(qū)执隧,即顯示屏幕揩抡。

Surface:Android應(yīng)用的每個(gè)窗口對(duì)應(yīng)一個(gè)畫(huà)布(Canvas),即Surface镀琉,可以理解為Android應(yīng)用程序的一個(gè)窗口峦嗤。

Android應(yīng)用程序的顯示過(guò)程包含了兩個(gè)部分(應(yīng)用側(cè)繪制、系統(tǒng)側(cè)渲染)屋摔、兩個(gè)機(jī)制(進(jìn)程間通訊機(jī)制烁设、顯示刷新機(jī)制),接下來(lái)我們就來(lái)一一道來(lái)钓试。

應(yīng)用側(cè)

一個(gè)Android應(yīng)用程序窗口里面包含了很多UI元素署尤,這些UI元素是以樹(shù)形結(jié)構(gòu)來(lái)組織的,即它們存在著父子關(guān)系亚侠,其中曹体,子UI元素位于父UI元素里面,如下圖:

因此硝烂,在繪制一個(gè)Android應(yīng)用程序窗口的UI之前箕别,我們首先要確定它里面的各個(gè)子UI元素在父UI元素里面的大小以及位置。確定各個(gè)子UI元素在父UI元素里面的大小以及位置的過(guò)程又稱(chēng)為測(cè)量過(guò)程和布局過(guò)程。因此串稀,Android應(yīng)用程序窗口的UI渲染過(guò)程可以分為測(cè)量除抛、布局和繪制三個(gè)階段。如下圖所示:

測(cè)量:遞歸(深度優(yōu)先)確定所有視圖的大心附亍(高到忽、寬)

布局:遞歸(深度優(yōu)先)確定所有視圖的位置(左上角坐標(biāo))

繪制:在畫(huà)布canvas上繪制應(yīng)用程序窗口所有的視圖

測(cè)量、布局沒(méi)有太多要說(shuō)的清寇,這里要著重說(shuō)一下繪制喘漏。Android目前有兩種繪制模型:基于軟件的繪制模型和硬件加速的繪制模型(從Android 3.0開(kāi)始全面支持)。

在基于軟件的繪制模型下华烟,CPU主導(dǎo)繪圖翩迈,視圖按照兩個(gè)步驟繪制:

1.讓View層次結(jié)構(gòu)失效

2.繪制View層次結(jié)構(gòu)

當(dāng)應(yīng)用程序需要更新它的部分UI時(shí),都會(huì)調(diào)用內(nèi)容發(fā)生改變的View對(duì)象的invalidate()方法盔夜。無(wú)效(invalidation)消息請(qǐng)求會(huì)在View對(duì)象層次結(jié)構(gòu)中傳遞负饲,以便計(jì)算出需要重繪的屏幕區(qū)域(臟區(qū))。然后喂链,Android系統(tǒng)會(huì)在View層次結(jié)構(gòu)中繪制所有的跟臟區(qū)相交的區(qū)域返十。不幸的是,這種方法有兩個(gè)缺點(diǎn):

1.繪制了不需要重繪的視圖(與臟區(qū)域相交的區(qū)域)

2.掩蓋了一些應(yīng)用的bug(由于會(huì)重繪與臟區(qū)域相交的區(qū)域)

注意:在View對(duì)象的屬性發(fā)生變化時(shí)椭微,如背景色或TextView對(duì)象中的文本等洞坑,Android系統(tǒng)會(huì)自動(dòng)的調(diào)用該View對(duì)象的invalidate()方法。

在基于硬件加速的繪制模式下赏表,GPU主導(dǎo)繪圖检诗,繪制按照三個(gè)步驟繪制:

1.讓View層次結(jié)構(gòu)失效

2.記錄匈仗、更新顯示列表

3.繪制顯示列表

這種模式下瓢剿,Android系統(tǒng)依然會(huì)使用invalidate()方法和draw()方法來(lái)請(qǐng)求屏幕更新和展現(xiàn)View對(duì)象。但Android系統(tǒng)并不是立即執(zhí)行繪制命令悠轩,而是首先把這些View的繪制函數(shù)作為繪制指令記錄一個(gè)顯示列表中间狂,然后再讀取顯示列表中的繪制指令調(diào)用OpenGL相關(guān)函數(shù)完成實(shí)際繪制。另一個(gè)優(yōu)化是火架,Android系統(tǒng)只需要針對(duì)由invalidate()方法調(diào)用所標(biāo)記的View對(duì)象的臟區(qū)進(jìn)行記錄和更新顯示列表鉴象。沒(méi)有失效的View對(duì)象則能重放先前顯示列表記錄的繪制指令來(lái)進(jìn)行簡(jiǎn)單的重繪工作。

使用顯示列表的目的是何鸡,把視圖的各種繪制函數(shù)翻譯成繪制指令保存起來(lái)纺弊,對(duì)于沒(méi)有發(fā)生改變的視圖把原先保存的操作指令重新讀取出來(lái)重放一次就可以了,提高了視圖的顯示速度骡男。而對(duì)于需要重繪的View淆游,則更新顯示列表,以便下次重用,然后再調(diào)用OpenGL完成繪制犹菱。

硬件加速提高了Android系統(tǒng)顯示和刷新的速度拾稳,但它也不是萬(wàn)能的,它有三個(gè)缺陷:

1.兼容性(部分繪制函數(shù)不支持或不完全硬件加速腊脱,參見(jiàn)文章尾)

2.內(nèi)存消耗(OpenGL API調(diào)用就會(huì)占用8MB访得,而實(shí)際上會(huì)占用更多內(nèi)存)

3.電量消耗(GPU耗電)

系統(tǒng)側(cè)

Android應(yīng)用程序在圖形緩沖區(qū)中繪制好View層次結(jié)構(gòu)后,這個(gè)圖形緩沖區(qū)會(huì)被交給SurfaceFlinger服務(wù)陕凹,而SurfaceFlinger服務(wù)再使用OpenGL圖形庫(kù)API來(lái)將這個(gè)圖形緩沖區(qū)渲染到硬件幀緩沖區(qū)中悍抑。

由于Android應(yīng)用程序很少能涉及到Android系統(tǒng)底層,所以SurfaceFlinger服務(wù)的執(zhí)行過(guò)程不做過(guò)多的介紹捆姜。

進(jìn)程間通訊機(jī)制

Android應(yīng)用程序?yàn)榱四軌驅(qū)⒆约旱腢I繪制在系統(tǒng)的幀緩沖區(qū)上传趾,它們就必須要與SurfaceFlinger服務(wù)進(jìn)行通信,如圖所示:

Android應(yīng)用程序與SurfaceFlinger服務(wù)是運(yùn)行在不同的進(jìn)程中的泥技,因此浆兰,它們采用某種進(jìn)程間通信機(jī)制來(lái)進(jìn)行通信。由于Android應(yīng)用程序在通知SurfaceFlinger服務(wù)來(lái)繪制自己的UI的時(shí)候珊豹,需要將UI數(shù)據(jù)傳遞給SurfaceFlinger服務(wù)簸呈,例如,要繪制UI的區(qū)域店茶、位置等信息蜕便。一個(gè)Android應(yīng)用程序可能會(huì)有很多個(gè)窗口,而每一個(gè)窗口都有自己的UI數(shù)據(jù)贩幻,因此轿腺,Android系統(tǒng)的匿名共享內(nèi)存機(jī)制就派上用場(chǎng)了。

每一個(gè)Android應(yīng)用程序與SurfaceFlinger服務(wù)之間丛楚,都會(huì)通過(guò)一塊匿名共享內(nèi)存來(lái)傳遞UI數(shù)據(jù)族壳,如下所示:

但是單純的匿名共享內(nèi)存在傳遞多個(gè)窗口數(shù)據(jù)時(shí)缺乏有效的管理,所以匿名共享內(nèi)存就被抽象為一個(gè)更上流的數(shù)據(jù)結(jié)構(gòu)SharedClient趣些,如下圖所示:

在每個(gè)SharedClient中仿荆,最多有31個(gè)SharedBufferStack,每個(gè)SharedBufferStack都對(duì)應(yīng)一個(gè)Surface坏平,即一個(gè)窗口拢操。這樣,我們就可以知道為什么每一個(gè)SharedClient里面包含的是一系列SharedBufferStack而不是單個(gè)SharedBufferStack:一個(gè)SharedClient對(duì)應(yīng)一個(gè)Android應(yīng)用程序舶替,而一個(gè)Android應(yīng)用程序可能包含有多個(gè)窗口令境,即Surface。從這里也可以看出顾瞪,一個(gè)Android應(yīng)用程序至多可以包含31個(gè)窗口舔庶。

每個(gè)SharedBufferStack中又包含了N個(gè)緩沖區(qū)(<4.1 N=2; >=4.1 N=3),即顯示刷新機(jī)制中即將提到的雙緩沖和三重緩沖技術(shù)返劲。

顯示刷新機(jī)制

一般我們?cè)诶L制UI的時(shí)候,都會(huì)采用一種稱(chēng)為“雙緩沖”的技術(shù)栖茉。雙緩沖意味著要使用兩個(gè)緩沖區(qū)(SharedBufferStack中)篮绿,其中一個(gè)稱(chēng)為Front Buffer,另外一個(gè)稱(chēng)為Back Buffer吕漂。UI總是先在Back Buffer中繪制亲配,然后再和Front Buffer交換,渲染到顯示設(shè)備中惶凝。理想情況下吼虎,這樣一個(gè)刷新會(huì)在16ms內(nèi)完成(60FPS),下圖就是描述的這樣一個(gè)刷新過(guò)程(Display處理前Front Buffer苍鲜,CPU思灰、GPU處理Back Buffer。

但現(xiàn)實(shí)情況并非這么理想混滔。

1.時(shí)間從0開(kāi)始洒疚,進(jìn)入第一個(gè)16ms:Display顯示第0幀,CPU處理完第一幀后坯屿,GPU緊接其后處理繼續(xù)第一幀油湖。三者互不干擾,一切正常领跛。

2.時(shí)間進(jìn)入第二個(gè)16ms:因?yàn)樵缭谏弦粋€(gè)16ms時(shí)間內(nèi)乏德,第1幀已經(jīng)由CPU,GPU處理完畢吠昭。故Display可以直接顯示第1幀喊括。顯示沒(méi)有問(wèn)題。但在本16ms期間矢棚,CPU和GPU卻并未及時(shí)去繪制第2幀數(shù)據(jù)(注意前面的空白區(qū))郑什,而是在本周期快結(jié)束時(shí),CPU/GPU才去處理第2幀數(shù)據(jù)幻妓。

3.時(shí)間進(jìn)入第3個(gè)16ms蹦误,此時(shí)Display應(yīng)該顯示第2幀數(shù)據(jù)劫拢,但由于CPU和GPU還沒(méi)有處理完第2幀數(shù)據(jù)肉津,故Display只能繼續(xù)顯示第一幀的數(shù)據(jù),結(jié)果使得第1幀多畫(huà)了一次(對(duì)應(yīng)時(shí)間段上標(biāo)注了一個(gè)Jank)舱沧。

通過(guò)上述分析可知妹沙,此處發(fā)生Jank的關(guān)鍵問(wèn)題在于,為何第1個(gè)16ms段內(nèi)熟吏,CPU/GPU沒(méi)有及時(shí)處理第2幀數(shù)據(jù)距糖?原因很簡(jiǎn)單玄窝,CPU可能是在忙別的事情,不知道該到處理UI繪制的時(shí)間了悍引《髦可CPU一旦想起來(lái)要去處理第2幀數(shù)據(jù),時(shí)間又錯(cuò)過(guò)了趣斤!

為解決這個(gè)問(wèn)題俩块,Android 4.1中引入了VSYNC,這類(lèi)似于時(shí)鐘中斷浓领。結(jié)果如下圖所示:

由上圖可知玉凯,每收到VSYNC中斷,CPU就開(kāi)始處理各幀數(shù)據(jù)联贩。整個(gè)過(guò)程非常完美漫仆。

不過(guò),仔細(xì)琢磨后卻會(huì)發(fā)現(xiàn)一個(gè)新問(wèn)題:上圖中泪幌,CPU和GPU處理數(shù)據(jù)的速度似乎都能在16ms內(nèi)完成盲厌,而且還有時(shí)間空余,也就是說(shuō)祸泪,CPU/GPU的FPS(幀率狸眼,F(xiàn)rames Per Second)要高于Display的FPS。確實(shí)如此浴滴。由于CPU/GPU只在收到VSYNC時(shí)才開(kāi)始數(shù)據(jù)處理拓萌,故它們的FPS被拉低到與Display的FPS相同。但這種處理并沒(méi)有什么問(wèn)題升略,因?yàn)锳ndroid設(shè)備的Display FPS一般是60微王,其對(duì)應(yīng)的顯示效果非常平滑。

如果CPU/GPU的FPS小于Display的FPS品嚣,會(huì)是什么情況呢炕倘?請(qǐng)看下圖:

由上圖可知:

1.在第二個(gè)16ms時(shí)間段,Display本應(yīng)顯示B幀翰撑,但卻因?yàn)镚PU還在處理B幀罩旋,導(dǎo)致A幀被重復(fù)顯示。

2.同理眶诈,在第二個(gè)16ms時(shí)間段內(nèi)涨醋,CPU無(wú)所事事,因?yàn)锳 Buffer被Display在使用逝撬。B Buffer被GPU在使用浴骂。注意,一旦過(guò)了VSYNC時(shí)間點(diǎn)宪潮,CPU就不能被觸發(fā)以處理繪制工作了溯警。

為什么CPU不能在第二個(gè)16ms處開(kāi)始繪制工作呢趣苏?原因就是只有兩個(gè)Buffer(Android 4.1之前)。如果有第三個(gè)Buffer的存在梯轻,CPU就能直接使用它食磕,而不至于空閑。出于這一思路就引出了三重緩沖區(qū)(Android 4.1)喳挑。結(jié)果如下圖所示:

由上圖可知:

第二個(gè)16ms時(shí)間段芬为,CPU使用C Buffer繪圖。雖然還是會(huì)多顯示A幀一次蟀悦,但后續(xù)顯示就比較順暢了媚朦。

是不是Buffer越多越好呢?回答是否定的日戈。由上圖可知询张,在第二個(gè)時(shí)間段內(nèi),CPU繪制的第C幀數(shù)據(jù)要到第四個(gè)16ms才能顯示浙炼,這比雙Buffer情況多了16ms延遲份氧。所以,Buffer最好還是兩個(gè)弯屈,三個(gè)足矣蜗帜。

到這里,Android系統(tǒng)的顯示原理就介紹完了资厉。那么在了解這些原理后對(duì)我們的流暢度測(cè)試有哪些幫助呢厅缺,請(qǐng)看我的下篇文章《Android應(yīng)用流暢度測(cè)試分析》。

附:不同的API Level下宴偿,繪制函數(shù)對(duì)硬件加速模式的支持情況

原文鏈接

Android顯示原理簡(jiǎn)介

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末湘捎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子窄刘,更是在濱河造成了極大的恐慌窥妇,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娩践,死亡現(xiàn)場(chǎng)離奇詭異活翩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)翻伺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)材泄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人穆趴,你說(shuō)我怎么就攤上這事脸爱∮龉” “怎么了未妹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵簿废,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我络它,道長(zhǎng)族檬,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任化戳,我火速辦了婚禮单料,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘点楼。我一直安慰自己扫尖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布掠廓。 她就那樣靜靜地躺著换怖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蟀瞧。 梳的紋絲不亂的頭發(fā)上沉颂,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音悦污,去河邊找鬼铸屉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛切端,可吹牛的內(nèi)容都是我干的彻坛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼踏枣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼小压!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起椰于,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怠益,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瘾婿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜻牢,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年偏陪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抢呆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笛谦,死狀恐怖抱虐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饥脑,我是刑警寧澤恳邀,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布懦冰,位于F島的核電站,受9級(jí)特大地震影響谣沸,放射性物質(zhì)發(fā)生泄漏刷钢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一乳附、第九天 我趴在偏房一處隱蔽的房頂上張望内地。 院中可真熱鬧,春花似錦赋除、人聲如沸阱缓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茬祷。三九已至,卻和暖如春并蝗,著一層夾襖步出監(jiān)牢的瞬間祭犯,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工滚停, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沃粗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓键畴,卻偏偏與公主長(zhǎng)得像最盅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子起惕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,095評(píng)論 25 707
  • 3.2 Android顯示原理 Android應(yīng)用程序的顯示過(guò)程包含了兩個(gè)部分(應(yīng)用側(cè)繪制涡贱、系統(tǒng)側(cè)渲染)、兩個(gè)機(jī)制...
    jianhuih閱讀 2,595評(píng)論 1 6
  • 注意事項(xiàng): 布局優(yōu)化惹想;盡量使用include问词、merge、ViewStub標(biāo)簽嘀粱,盡量不存在冗余嵌套及過(guò)于復(fù)雜布局(...
    HarryXR閱讀 5,167評(píng)論 1 19
  • 原文: http://www.cnblogs.com/yezhennan/p/5442031.html UI性能測(cè)...
    vb12閱讀 4,276評(píng)論 0 9
  • 你拘謹(jǐn)?shù)南癜褌?不哭泣激挪,也不胡鬧 靜靜地立著 望著人來(lái)人往 太陽(yáng)東升西落 平凡了寂寞 冷淡了憂(yōu)愁 倘若被人用時(shí) 那...
    錢(qián)遷閱讀 196評(píng)論 0 1