Preface
Android中,Client測量和計算布局,SurfaceFlienger(server)用來渲染繪制界面,client和server的是通過匿名共享內存(SharedClient)通信。
每個應用和SurfaceFlienger之間都會創建一個SharedClient,一個SharedClient最多可以創建31個SharedBufferStack,每個surface對應一個SharedBufferStack,也就是一個Window。也就意味着,每個應用最多可以創建31個窗口。
Android 4.1 之后,AndroidOS 團隊對Android Display進行了不斷地進化和改變。引入了三個核心元素:Vsync,Triple Butter,Choreographer。
首先來理解一下,圖形界面的繪制,大概是有CPU准備數據,然后通過驅動層把數據交給GPU來進行繪制。圖形API不允許CPU和GPU直接通信,所以就有了圖形驅動(Graphics Driver)來進行聯系。Graphics Driver維護了一個序列(Display List),CPU不斷把需要顯示的數據放進去,GPU不斷取出來進行顯示。
其中Choreographer起調度的作用。統一繪制圖像到Vsync的某個時間點。
Choreographer在收到Vsync信號時,調用用戶設置的回調函數。函數的先后順序如下:
CALLBACK_INPUT:與輸入事件有關
CALLBACK_ANIMATION:與動畫有關
CALLBACK_TRAVERSAL:與UI繪制有關
Vsync是什么呢?首先來說一下什么是FPS,FPS就是Frame Per Second(每秒的幀數)的縮寫,我們知道,FPS>=60時,我們就不會覺得動畫卡頓。當FPS=60時是個什么概念呢?1000/60≈16.6,也就是說在大概16ms中,我們要進行一次屏幕的刷新繪制。Vsync是垂直同步的縮寫。這里我們可以簡單的理解成,這就是一個時間中斷。例如,每16ms會有一個Vsync信號,那么系統在每次拿到Vsync信號時刷新屏幕,我們就不會覺得卡頓了。
但實現起來還是有點困難的。
多重緩沖是什么技術呢?我們先來說雙重緩沖。在Linux上,通常使用FrameBuffer來做顯示輸出。雙重緩沖會創建一個FrontBuffer和一個BackBuffer,顧名思義,FrontBuffer是當前顯示的頁面,BackBuffer是下一個要顯示的畫面。然后滾動電梯式顯示數據。為什么呢?這樣好在哪里呢?首先他並不是不卡了,他還是會卡。但是如果是單重緩沖,頁面可能會有這種情況:A面數據需要顯示,然后是B面數據顯示,B面數據顯示需要耗費一定時間,但是這個時間里,C面數據也請求了展示,我們可能會看到,在展示C面數據的時候,還有B面數據的殘影…
下面分情況來具體說明一下(Vsync每16秒一次)。
1.沒有使用Vsync的情況
可以看出,在第一個16ms之內,一切正常。然而在第二個16ms之內,幾乎是在時間段的最后CPU才計算出了數據,交給了Graphics Driver,導致GPU也是在第二段的末尾時間才進行了繪制,整個動作延后到了第三段內。從而影響了下一個畫面的繪制。這時會出現Jank(閃爍,可以理解為卡頓或者停頓)。那么在第二個16ms前半段的時間CPU和GPU干什么了?哦,他們可能忙別的事情了。這就是卡頓出現的原因和情況。CPU和GPU很隨意,愛什么時候刷新什么時候刷新,很隨意。
2.有Vsync的情況
如果,按照之前的前提來說,Vsync每16ms一次,那么在每次發出Vsync命令時,CPU都會進行刷新的操作。也就是在每個16ms的第一時間,CPU就會想贏Vsync的命令,來進行數據刷新的動作。CPU和GPU的刷新時間,和Display的FPS是一致的。因為只有到發出Vsync命令的時候,CPU和GPU才會進行刷新或顯示的動作。圖中是正常情況。那么不正常情況是怎么個情況?我們先來說一下雙重緩沖,然后再說。
3.雙重緩沖
邏輯就是和之前一樣。多重緩沖頁面在Back Buffer,然后根據需求來顯示不同數據。但是會有什么問題呢(這就是2中提到的問題)?
首先我們看Display行,A頁面需要了兩個時間單位,為什么?因為B Buffer在處理的時候太耗時了。然后導致了,在第一個Vsync發出的時候,還在GPU還在繪制B Buffer。那么,剛好,第一個Vsync發出之后很短的時間,A頁面展示完了,B Buffer的也在一開始的時候就不進行計算了。那么接下來的時間呢?屏幕還是展示着B Buffer,這時候就會造成Jank現象。他不會動,因為他在等下一個Vsync過來的時候,才會顯示下一個數據。
那么,如圖,在A Buffer過來的時候,展示B頁面的數據。這個時候!重復了上一個情況,也是太耗時了,然后又覆蓋了下一個Vysnc發
出的時間,再次造成卡頓!依次類推,會造成多次卡頓。這個時候就有了三重緩沖的概念。
4.三重緩沖
首先看圖。我們看到,B Buffer依舊很耗時,同樣覆蓋了第一個Vsync發出的時間點。但是,在第一個Vsync發出的時候,C Buffer站了出來,說,我來展示這個頁面,你去緩沖A后面需要緩沖的頁面吧!然后會發生什么?然后就是出現了一個Jank…,但是這個Jank只在這一個時間單位出現,是可以忽略不計的。因為之后的邏輯都是順暢的了。依次類推,除了A和B 兩個圖層在交替顯示,還有個“第三者”在不斷幫他們兩個可能需要展示的數據進行緩沖。但是注意了:
只有在需要時,才會進行三重緩沖。正常情況下,只使用二級緩沖!
另外,緩沖區不是越多越好。上圖,C頁面在第四個時間段才展示出來,就是因為中間多了一個Buffer(C Buffer)來進行緩沖。
但是,雖然谷歌給了你這么牛逼的前提邏輯,實際開發中你寫的APP還是會卡,為什么呢?原因大概有兩點:
1.界面太復雜。
2.主線程(UI線程)太忙。他可能還在處理用戶交互或者其他事情。