Preface Android中,Client測量和計算佈局,SurfaceFlienger(server)用來渲染繪製界面,client和server的是通過匿名共用記憶體(SharedClient)通信。 每個應用和SurfaceFlienger之間都會創建一個SharedClient,一個Sha ...
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線程)太忙。他可能還在處理用戶交互或者其他事情。