屏幕渲染機制
- cpu(gpu sf暫統(tǒng)稱cpu)經(jīng)過各種運算征峦,將數(shù)據(jù)寫入一塊內(nèi)存中馆纳,這塊內(nèi)存叫做「幀緩沖」敌厘。
- 幀緩沖可以理解為一個M*N矩陣滴须,數(shù)據(jù)從上到下一行一行保存舌狗。
- 屏幕在顯示的時候,從上到下逐行掃描扔水,依次顯示在屏幕上痛侍,我們把這樣的一屏數(shù)據(jù)叫做「一幀」。
- 當(dāng)一幀數(shù)據(jù)渲染完后魔市,就開始新一輪掃描主届,如果CPU「正好」(不正好后面再說)也把下一幀數(shù)據(jù)寫入幀緩沖赵哲,那么就會顯示下一幀畫面,如此循環(huán)君丁,我們就看到了不斷變化的畫面枫夺,也就是圖像。
過程很簡單谈截,執(zhí)行起來卻很難筷屡,因為這里有兩個問題,撕裂
和卡頓
簸喂,為什么毙死?
- 如果當(dāng)屏幕渲染到一半的時候,cpu已經(jīng)整理好了下一幀的數(shù)據(jù)喻鳄,給到幀緩沖扼倘,這時屏幕繼續(xù)從幀緩沖中一半的位置開始讀取數(shù)據(jù),這時畫面就會顯示異常除呵,這種兩個圖像重合的的問題叫作圖像
撕裂
再菊。 - 如果當(dāng)屏幕渲染完一幀畫面,cpu還沒整理好下一幀數(shù)據(jù)給到幀緩沖颜曾,這時屏幕繼續(xù)從幀緩沖中讀取數(shù)據(jù)渲染纠拔,顯示的還是上一幀的數(shù)據(jù),這時用戶就會感覺到
卡頓
泛豪。
那你就會有個疑問了
- 這cpu怎么這么笨稠诲,屏幕都沒把數(shù)據(jù)渲染完,干嘛把數(shù)據(jù)換了诡曙?
- 屏幕怎么這么傻臀叙,數(shù)據(jù)都被換了,怎么不從頭讀取呢价卤?
這是因為"現(xiàn)代計算機之父"馮·諾依曼提出了計算機的體系結(jié)構(gòu): 計算機由運算器劝萤,存儲器,控制器慎璧,輸入設(shè)備和輸出設(shè)備構(gòu)成床嫌,每部分各司其職。
- 屏幕的作用就是渲染胸私,不管外界怎么變化既鞠,它就是一行一行的渲染。
- cpu的任務(wù)就是運算盖文,運算好了把數(shù)據(jù)丟給幀緩沖嘱蛋,繼續(xù)運算。
那能不能讓cpu停下來?
當(dāng)然可以洒敏,但是不劃算龄恋,因為這樣就等于把cpu的長處給扼殺了。
等等凶伙,有個問題郭毕,你說當(dāng)兩個圖像重合的的問題叫作撕裂
,那不是從第二幀開始函荣,時時刻刻都在撕裂
?
- 好吧显押,我說錯了,有一點忘記說了傻挂,應(yīng)該有個時間乘碑,在這個時間內(nèi)渲染完一幀,畫面看起來就是連續(xù)的金拒,這個時間大約是16ms兽肤。
為什么是16ms?
- 這是因為人眼與大腦之間的協(xié)作無法感知超過60fps的畫面更新。12fps大概類似手動快速翻動書籍的幀率绪抛,這明顯是可以感知到不夠順滑的资铡。24fps使得人眼感知的是連續(xù)線性的運動,這其實是歸功于運動模糊的效果幢码。24fps是電影膠圈通常使用的幀率笤休,因為這個幀率已經(jīng)足夠支撐大部分電影畫面需要表達的內(nèi)容,同時能夠最大的減少費用支出症副。但是低于30fps是無法順暢表現(xiàn)絢麗的畫面內(nèi)容的宛官,此時就需要用到60fps來達到想要的效果,當(dāng)然超過60fps是沒有必要的瓦糕。
fps是啥玩意兒?這里說兩個概念
屏幕刷新率(Hz):屏幕在一秒內(nèi)刷新的次數(shù)腋么,Android手機一般都是60Hz咕娄,也就是一秒刷新60次,當(dāng)然也有高刷的珊擂,但是60Hz足矣圣勒。
幀速率(FPS):cpu在一秒內(nèi)合成的幀數(shù),比如60FPS摧扇,就是60 frame per sconds圣贸,意思就是一秒合成60幀。如上所述扛稽,當(dāng)屏幕刷新率大于幀速率的時候吁峻,會發(fā)生卡頓;屏幕刷新率小于幀速率的時候,會發(fā)生撕裂用含。那么怎么解決這個問題呢矮慕?
怎么解決呢?Vsync 和 雙緩沖
- 屏幕正在從前緩沖讀取第一幀數(shù)據(jù)并渲染啄骇,此時cpu計算完第二幀數(shù)據(jù)痴鳄,放在后緩沖,等待VSYNC信號缸夹。
- 屏幕將第一幀數(shù)據(jù)渲染完畢痪寻,發(fā)出VSYNC信號,cpu收到VSYNC信號虽惭,將后緩沖的第二幀數(shù)據(jù)復(fù)制到前緩沖橡类。
- 同時屏幕繼續(xù)繪制第二幀數(shù)據(jù),cpu開始計算下一幀數(shù)據(jù)趟妥,循環(huán)往復(fù)猫态。
牛~~,雙緩沖+Vsync解決了撕裂問題披摄,但是并沒有解決卡頓的問題,當(dāng)cpu收到Vsync信號后亲雪,如果cpu還沒有計算完,也肯定就不會交換前后緩沖的數(shù)據(jù)疚膊,也就是說义辕,屏幕再次讀取的還是前緩沖的數(shù)據(jù),也就是兩次顯示了一樣的畫面寓盗,也就是產(chǎn)生了卡頓灌砖。
是的,卡頓問題是解決不了的傀蚌,只能優(yōu)化基显。
還能怎么優(yōu)化?三緩沖 善炫!
首先要清楚下撩幽,Android屏幕繪制流程。
- 任何一個View都是依附于window的
- 一個window對應(yīng)一個surface
- view的measure箩艺、layout窜醉、draw等均是計算數(shù)據(jù),這些是cpu干的事
- cpu把這些事干好后艺谆,在經(jīng)過一系列計算將數(shù)據(jù)轉(zhuǎn)交給gpu
- gpu將數(shù)據(jù)柵格化后榨惰,就交給SurfeceFlinger(以下簡稱SF)
- SF將多個surfece數(shù)據(jù)合并處理后,就放入后緩沖區(qū)
- 屏幕以固定頻率從前緩沖區(qū)拿出數(shù)據(jù)渲染静汤,渲染完畢后發(fā)送VSYNC琅催,此時前后緩沖區(qū)數(shù)據(jù)交換居凶,屏幕繪制下一幀。
上述7步是建立在開啟硬件加速的情況下的恢暖,如果沒有硬件加速排监,就去掉gpu部分,就可以簡單理解為cpu直接將數(shù)據(jù)轉(zhuǎn)交給sf杰捂,我們簡單整理一下數(shù)據(jù)的傳遞流程:
「cpu -> gpu -> display」
舆床,而且我們看到,cpu和gpu是排隊工作的嫁佳,它倆和屏幕是并行工作的挨队。好,我們來看發(fā)生卡頓(jank)的場景
- 我們可以將Display那一行看作是前緩沖蒿往,將GPU和CPU兩行疊加起來看作是后緩沖(因為它倆排隊使用)盛垦,將VSYNC線隔離開的豎行看作一個幀。
- 我們看到瓤漏,在第一幀里面腾夯,GPU墨跡了半天沒搞完,以至于在第二幀里面蔬充,Display(屏幕)顯示的還是第一幀的A數(shù)據(jù)蝶俱,此時就產(chǎn)生了Jank(卡頓),并且在一個vsync信號過來后饥漫,cpu什么都沒做榨呆,因為gpu占著后緩沖(那個綠色的長B塊),所以cpu只能再等下一個vsync庸队,在下一個vsync里面积蜻,cpu終于拿到了后緩沖的使用權(quán),但是cpu計算時間比較長彻消,導(dǎo)致了gpu時間不夠用竿拆,數(shù)據(jù)又沒算完,再次發(fā)生了卡頓宾尚,可以說丙笋,這次卡頓直接受到了第一次卡頓的影響。
- 試想: 如果在第一次卡頓的時候央勒,cpu也能計算數(shù)據(jù),那么澳化,第二次卡頓可能就不存在了崔步,因為cpu已經(jīng)在第一次卡頓的時候把藍色的A給計算完了,第二次完全可以讓gpu獨自計算(綠色的A)缎谷,就不存在因為排隊導(dǎo)致的時間不夠用了井濒,但是灶似!cpu和gpu共用后緩沖,這就導(dǎo)致它們只能輪流使用后緩沖瑞你,怎么解決呢酪惭?再加一個后緩沖區(qū),讓cpu者甲、gpu各用一塊春感。
我們來看引入三緩沖后的效果:
- 我們看到,在第一次jank內(nèi)虏缸,cpu使用了第三塊緩沖區(qū)鲫懒,自己計算了C幀的數(shù)據(jù),假如此時沒有三緩沖刽辙,那么cpu就只能再繼續(xù)等下一個vsync信號窥岩,也就是在圖中藍色A塊的地方,才能開始計算C幀數(shù)據(jù)宰缤,就又引發(fā)下一次卡頓颂翼。我們看到,通過引入三緩沖慨灭,雖然不能避免卡頓問題朦乏,但是卻可以大幅優(yōu)化卡頓問題,尤其是避免連續(xù)卡頓缘挑。
- 但是集歇,三緩沖也有缺點,就是耗資源语淘,所以系統(tǒng)并非一直開啟三緩沖诲宇,要想真正解決問題,還需要在cpu層對數(shù)據(jù)盡量優(yōu)化惶翻,從而減小cpu和gpu的計算量姑蓝,比如:View盡量扁平化,少嵌套吕粗,少在UI線程做耗時操作等纺荧。
源碼層面看cpu做了哪些事
請看這篇文章,View的加載流程
從這里我們可以看出颅筋,View加載過程中宙暇,使用到的耗時操作
- IO操作(讀取布局文件)
- Xml解析(解析布局文件)
- 使用了遞歸(遞歸查找每一個View)。
- 反射(使用反射初始化view)
解決IO操作和Xml解析
如果在可以的情況下议泵,不推薦使用Xml來寫布局占贫,推薦使用代碼直接new。但是這樣一來可維護性大大降低先口。
借用此思想型奥,github上也有一個由掌閱發(fā)布的開源庫:https://github.com/iReaderAndroid/X2C
- 為了即保留xml的優(yōu)點瞳收,又解決它帶來的性能問題,我們開發(fā)了X2C方案厢汹。即在編譯生成APK期間螟深,將需要翻譯的layout翻譯生成對應(yīng)的java文件,這樣對于開發(fā)人員來說寫布局還是寫原來的xml烫葬,但對于程序來說界弧,運行時加載的是對應(yīng)的java文件。 ????我們采用APT(Annotation Processor Tool)+ JavaPoet技術(shù)來完成編譯期間【注解】->【解注解】->【翻譯xml】->【生成java】整個流程的操作厘灼。
RecyclerView優(yōu)化
RecyclerView一些你可能需要知道的優(yōu)化技術(shù)
參考:
Android 性能優(yōu)化必知必會
性能優(yōu)化系列總篇
Android性能優(yōu)化
一篇小短文夹纫,帶你了解屏幕刷新背后的故事
Android UI性能優(yōu)化實戰(zhàn) 識別繪制中的性能問題
Android UI性能優(yōu)化 檢測應(yīng)用中的UI卡頓
Google 發(fā)布 Android 性能優(yōu)化典范
Android性能優(yōu)化