關(guān)于Android中16ms的問題

? ? ? ?如果你覺得你的應(yīng)用界面出現(xiàn)卡頓不流暢的情況咆畏,不用懷疑旧找,這很大原因是你沒有在16ms完成你的工作钮蛛。沒錯(cuò)愿卒,16ms要完成你的工作琼开,再慢點(diǎn)柜候,用戶一定會吐槽渣刷,然后狠心把你辛辛苦苦開發(fā)出來的應(yīng)用給卸載掉辅柴,你也不想想碌嘀,人生有幾個(gè)16ms可以浪費(fèi)肮扇摺!

你應(yīng)該知道的16ms問題

? ? ? ?上面說的是APP渲染性能問題烹棉,現(xiàn)在的APP為了提高用戶的體驗(yàn)浆洗,都喜歡加入很多酷炫的動效伏社。實(shí)現(xiàn)這些動效意味著要消耗系統(tǒng)更多的性能洛口。如果處理不好,Android系統(tǒng)可能會無法及時(shí)完成這些復(fù)雜的動畫和界面的渲染买优,從而導(dǎo)致卡頓問題的出現(xiàn)。

??那么上面說的16ms是什么意思湘纵,為什么要在16ms內(nèi)完成我們的工作呢梧喷,這是因?yàn)榇蠖鄶?shù)的Android顯示屏幕是以每秒60幀來刷新的(也就是60Hz)铺敌。一幀可以看做是一張的獨(dú)立圖片偿凭,60幀每秒就意味著:16ms=1000/60Hz弯囊,相當(dāng)于60fps匾嘱。這就是上面說的16ms奄毡,這也是為什么Android系統(tǒng)每隔16ms就會發(fā)出一次VSYNC信號觸發(fā)對UI進(jìn)行渲染吼过,如果這16ms內(nèi)我們沒有完成對視圖的繪制盗忱,那么就會出現(xiàn)丟幀的情況趟佃。也許有人會問Android手機(jī)的屏幕為什么是每秒刷新60幀(60fps)昧捷,專家是這么解釋的:

這是因?yàn)槿搜叟c大腦之間的協(xié)作無法感知超過60fps的畫面更新靡挥。12fps大概類似手動快速翻動書籍的幀率跋破,這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續(xù)線性的運(yùn)動舷手,這其實(shí)是歸功于運(yùn)動模糊的 效果男窟。24fps是電影膠圈通常使用的幀率蝎宇,因?yàn)檫@個(gè)幀率已經(jīng)足夠支撐大部分電影畫面需要表達(dá)的內(nèi)容姥芥,同時(shí)能夠最大的減少費(fèi)用支出凉唐。但是低于30fps是 無法順暢表現(xiàn)絢麗的畫面內(nèi)容的台囱,此時(shí)就需要用到60fps來達(dá)到想要的效果簿训,當(dāng)然超過60fps是沒有必要的(據(jù)說Dart能夠帶來120fps的體驗(yàn))

好强品,聽完專家解釋后的榛,那么卡頓問題到底是怎樣產(chǎn)生呢夫晌,想要知道這個(gè)晓淀,我們需要簡單了解一下Android的渲染機(jī)制凶掰。

從xml到display

??我們平時(shí)寫的那些xml布局到底是怎樣繪制到屏幕上的呢锄俄?我們一般不太喜歡關(guān)注這些問題奶赠,因?yàn)檫@些android系統(tǒng)通通都會幫我們搞掂毅戈。是吧苇经,程序猿一般比較懶扇单,能多省事就多省事蜘澜,但是今天我們還是有必要了解一下鄙信,先看看下面的圖:

Activity的界面之所以可以被繪制到屏幕上其中有一個(gè)很重要的過程就是柵格化(Resterization装诡,柵格化簡單來說就是將向量圖轉(zhuǎn)化為機(jī)器可以識別的位圖的一個(gè)過程鸦采。其中很復(fù)雜也比較很耗時(shí)赖淤,GPU就是用來加快柵格化的速度咱旱。從上面的圖可以看出吐限,CPU會先把UI組件計(jì)算成polygons(多邊形)和textures(紋理)诸典,然后再交給GPU進(jìn)行柵格化渲染狐粱,最后GPU再將數(shù)據(jù)傳送給屏幕肌蜻,由屏幕進(jìn)行繪制顯示蒋搜。當(dāng)然豆挽,從CPU到GPU還需要經(jīng)過OpenGL ES的處理帮哈,這也是一個(gè)很復(fù)雜的過程娘侍。對OpenGL有興趣的童鞋,想更加深入了解的話僵缺,可以自行度娘磕潮,查看官方文檔或者源碼什么的自脯,這里就不細(xì)說了膏潮,因?yàn)槲乙膊皇呛芏啦危瑖濉?br>

關(guān)于VSYNC

??接下來要聊聊VSYNC叠纷,VSYNC這個(gè)概念出來很久了涩嚣,Vertical Synchronization顷歌,就是所謂的“垂直同步”眯漩。在Android中這也沿用了這個(gè)概念,我們也可以把它理解為“幀同步”冯勉。這個(gè)用來干嘛的呢灼狰,就是為了保證CPU交胚、GPU生成幀的速度和display刷新的速度保持一致蝴簇,Android系統(tǒng)每16ms就會發(fā)出一次VSYNC信號觸發(fā)UI渲染更新熬词。上面提到屏幕一秒刷新60次互拾,這就要求CPU和GPU每秒要有處理60幀的能力颜矿,一幀花費(fèi)的時(shí)間在16ms內(nèi)骑疆。那么在Android系統(tǒng)中箍铭,是如何利用VSYNC工作的呢坡疼,如下圖:

從上圖我們可以知道當(dāng)上一幀顯示結(jié)束后闸氮,在VSYNC信號剛開始發(fā)出時(shí)蒲跨,Android系統(tǒng)就立刻開始了下一幀數(shù)據(jù)的處理了或悲,這樣就不會浪費(fèi)時(shí)間了巡语。圖中先顯示第0幀男公,在這16ms顯示時(shí)間里,CPU和GPU已經(jīng)開始準(zhǔn)備下一幀的數(shù)據(jù)了澄阳,趕在下個(gè)VSYNC信號到來時(shí)碎赢,GPU渲染完成揩抡,及時(shí)交換數(shù)據(jù)峦嗤,display繪制顯示完成烁设,不出什么意外的話装黑,每一幀都這么井然有序進(jìn)行著恋谭,那么用戶就會體驗(yàn)到那如絲順滑般的感覺的了疚颊,這是多美妙的事情啊均抽!

雙緩沖機(jī)制

??其實(shí)上面說的就是Android的雙緩沖機(jī)制油挥,而雙緩沖技術(shù)一直貫穿這個(gè)Android系統(tǒng)深寥。因?yàn)閷?shí)際上幀的數(shù)據(jù)就是保存在兩個(gè)緩沖區(qū)中惋鹅,A緩沖用來顯示當(dāng)前幀负饲,那么B緩沖就用來緩存下一幀的數(shù)據(jù)喂链,這樣就可以做到一邊顯示一邊處理下一幀的數(shù)據(jù)椭微。

前面的幀用序號表示,但實(shí)際上幀數(shù)據(jù)只保存在A本慕、B兩個(gè)緩沖區(qū)中锅尘。當(dāng)前幀顯示緩沖A藤违,Android系統(tǒng)一旦發(fā)出VSYN信號時(shí)顿乒,就會在緩沖B中構(gòu)建新的幀璧榄。當(dāng)完成后(這里的完成指的是屏幕已經(jīng)在緩沖B中拿到新一幀的數(shù)據(jù),完成繪制)拾稳,緩沖A的數(shù)據(jù)就會被清空访得,繼續(xù)進(jìn)行下一幀的繪制悍抑,注意搜骡,此時(shí)緩沖B的數(shù)據(jù)是不會被清空的记靡,因?yàn)楫?dāng)前顯示的是緩沖B中幀畫面摸吠,清空的只是緩沖A的數(shù)據(jù)寸痢。

??這樣看起來貌似沒什么問題啼止,一切都是我們的掌控中献烦。但是巩那,由于某些原因拢操,比如我們應(yīng)用代碼上處理不夠好舶替,又或者用戶手機(jī)后臺打開了很多應(yīng)用顾瞪,又在聽歌又在下載視頻什么的,CPU一時(shí)間被占用了瞧甩,導(dǎo)致下一幀繪制的時(shí)間超過了16ms肚逸,那么問題就來了朦促,這時(shí)候用戶就不爽了务冕,因?yàn)橛脩艉苊黠@感知到了卡頓的出現(xiàn)禀忆,也就是所謂的丟幀情況箩退。如下圖所示:

很好魂毁,下面我們來認(rèn)真分析一下為什么會出現(xiàn)丟幀的情況:

Step1. 當(dāng)Display顯示第0幀數(shù)據(jù)胧瓜,此時(shí)CPU和GPU已經(jīng)開始渲染第1幀畫面府喳,并將數(shù)據(jù)緩存在緩沖B中钝满;

Step2. 但是由于某些原因弯蚜,就好像上面說的碎捺,CPU資源一時(shí)間被占用,導(dǎo)致系統(tǒng)處理該幀數(shù)據(jù)耗時(shí)過長或者未能及時(shí)處理該幀數(shù)據(jù)晋柱;

Step3. 當(dāng)VSYNC信號來時(shí)雁竞,display向B緩沖要數(shù)據(jù)碑诉,這下悲催了联贩,因?yàn)榫彌_B的數(shù)據(jù)還沒準(zhǔn)備好泪幌,B緩沖區(qū)這時(shí)候是被鎖定的祸泪,display無可奈何没隘,只能繼續(xù)顯示之前緩沖A的那一幀右蒲,此時(shí)緩沖A的數(shù)據(jù)也不能被清空和交換數(shù)據(jù)瑰妄。這種情況被Android開發(fā)組命名為“Jank”间坐,就是所謂的“丟幀”,也被稱作“廢幀”劳澄;

Step4. 當(dāng)?shù)?幀數(shù)據(jù)(即緩沖B數(shù)據(jù))準(zhǔn)備完成后秒拔,它并不會馬上被顯示溯警,而是要等待下一個(gè)VSYNC梯轻,Display刷新后喳挑,這時(shí)用戶才看到畫面的更新伊诵,中間這段時(shí)間的時(shí)間就白白被浪費(fèi)掉了曹宴。

??從上面的分析可以知道笛坦,因?yàn)榫彌_B的超時(shí)版扩,掉了鏈子礁芦,導(dǎo)致出現(xiàn)了丟幀的情況。因?yàn)橐徊降难舆t肖方,也很有可能導(dǎo)致后面的處理延遲窥妇,很可能造成一步慢步步慢啊,像你這樣“延誤工期”在古代可是大罪啊烹骨,分分鐘要?dú)㈩^的哦~~~

三倍緩沖機(jī)制

??出現(xiàn)上面這種情況怎么辦吨岭,在Android系統(tǒng)里給出了這樣的解決辦法就是:再加入一個(gè)緩沖峦树。這樣就出現(xiàn)了三個(gè)緩沖,顧名思義姐浮,這里說的就是三倍緩沖卖鲤。好蛋逾,看下圖:

??當(dāng)出現(xiàn)B緩沖超時(shí),屏幕顯示的還是緩沖A中的那一幀区匣,因?yàn)榇藭r(shí)緩沖A的數(shù)據(jù)還在使用沉颂,不能及時(shí)被交換铸屉,所以在下一次VSYNC信號來之前這段時(shí)間無任何作為彻坛,時(shí)間就會白白被浪費(fèi)昌屉。為了避免這種時(shí)間浪費(fèi)间驮,在三倍緩沖機(jī)制中竞帽,系統(tǒng)這個(gè)時(shí)候會創(chuàng)建一個(gè)緩沖C屹篓,用來緩沖下一幀的數(shù)據(jù)堆巧。如上圖所示谍肤,顯示完緩沖B中那一幀后荒揣,下一幀就是顯示緩沖C中的了乳附。這樣雖然還是不能避免會出現(xiàn)卡頓的情況赋除,但是Android系統(tǒng)還是盡力去彌補(bǔ)這種缺陷举农,最終盡可能給用平滑的動效體驗(yàn)颁糟。

??關(guān)于Android的渲染機(jī)制其實(shí)是一個(gè)很復(fù)雜的過程棱貌,上面也只是就整體流程來論述婚脱,其中很多過程和細(xì)節(jié)都忽略了障贸,有興趣的童鞋自行深入研究哈篮洁。袁波。锋叨。下面給出一些簡單的優(yōu)化建議娃磺。

如何優(yōu)化16ms問題

1.盡可能減少Overdraw偷卧,就是減少過渡繪制听诸,減少布局嵌套的層次,去掉重復(fù)設(shè)置的背景桥嗤;

2.減少listview中g(shù)etView中的耗時(shí)操作泛领,一些自定義的view盡可能減少invalidate的調(diào)用渊鞋;

3.盡可能不要在UI線程做過多耗時(shí)的操作锡宋;

??16ms時(shí)間很短执俩,身為一名應(yīng)用開發(fā)者奠滑,為了讓用戶有更好的體驗(yàn)宋税,應(yīng)該要充分利用這16ms杰赛,確保刷新一幀的時(shí)候在16ms內(nèi)乏屯。上面只是幾條簡單的優(yōu)化建議辰晕,如果大家想要深入了解Android的性能優(yōu)化含友,可以參考我們Open軟件開發(fā)小組之前發(fā)布的幾篇關(guān)于Android性能優(yōu)化的文章辆童,這里就不細(xì)說了把鉴。感謝大家的支持庭砍,歡迎大家關(guān)注我們Open軟件開發(fā)小組的公眾號怠缸,更多優(yōu)質(zhì)文章與你分享凯旭。

參考文章

Android性能優(yōu)化典范

破譯Android性能優(yōu)化中的16ms問題 - 簡書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嫉柴,隨后出現(xiàn)的幾起案子计螺,更是在濱河造成了極大的恐慌登馒,老刑警劉巖陈轿,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麦射,死亡現(xiàn)場離奇詭異潜秋,居然都是意外死亡峻呛,警方通過查閱死者的電腦和手機(jī)杀饵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門切距,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谜悟,“玉大人葡幸,你說我怎么就攤上這事蔚叨∶锼” “怎么了搀别?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵歇父,是天一觀的道長榜苫。 經(jīng)常有香客問我垂睬,道長羔飞,這世上最難降的妖魔是什么逻淌? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任田柔,我火速辦了婚禮硬爆,結(jié)果婚禮上擎鸠,老公的妹妹穿的比我還像新娘劣光。我一直安慰自己绢涡,他們只是感情好雄可,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布聪舒。 她就那樣靜靜地躺著过椎,像睡著了一般疚宇。 火紅的嫁衣襯著肌膚如雪敷待。 梳的紋絲不亂的頭發(fā)上仁热,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天举哟,我揣著相機(jī)與錄音妨猩,去河邊找鬼壶硅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛椒舵,可吹牛的內(nèi)容都是我干的笔宿。 我是一名探鬼主播措伐,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捧存!你這毒婦竟也來了昔穴?” 一聲冷哼從身側(cè)響起吗货,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤笨腥,失蹤者是張志新(化名)和其女友劉穎脖母,沒想到半個(gè)月后谆级,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼积,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舆绎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年亿蒸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了边锁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茅坛。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡曹鸠,死狀恐怖斥铺,靈堂內(nèi)的尸體忽然破棺而出晾蜘,到底是詐尸還是另有隱情剔交,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站竭鞍,受9級特大地震影響笼蛛,放射性物質(zhì)發(fā)生泄漏滨砍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望响逢。 院中可真熱鬧舔亭,春花似錦、人聲如沸订雾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽识补。三九已至李请,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背白翻。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工滤馍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巢株,地道東北人阁苞。 一個(gè)月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓那槽,卻偏偏與公主長得像骚灸,于是被迫代替她去往敵國和親甚牲。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評論 25 707
  • 總結(jié)Android渲染部分的工作原理著恩,其中參考了如下網(wǎng)址:http://www.androidpolice.com...
    xuluqxulu閱讀 4,853評論 0 55
  • 60fps的由來: 作為程序員邀摆,我們經(jīng)常會聽到60fps和16ms這兩個(gè)重要值伍茄,同時(shí)我們會將程序是否達(dá)到60fps...
    Memebox閱讀 3,078評論 0 12
  • 3.2 Android顯示原理 Android應(yīng)用程序的顯示過程包含了兩個(gè)部分(應(yīng)用側(cè)繪制例获、系統(tǒng)側(cè)渲染)曹仗、兩個(gè)機(jī)制...
    jianhuih閱讀 2,604評論 1 6
  • 有個(gè)朋友說祥山,這幾日頗為浮躁,在拿到個(gè)人公號原創(chuàng)后澳窑,忙著在各大牛群里拉粉照捡、互粉话侧、刷閱讀、互贊悲立,卻忘了初心薪夕。并說,要反...
    李漁空間閱讀 585評論 9 6