Android Project Butter分析

Android Project Butter分析

一背景知識介紹

隨着時間的推移,Android OS系統一直在不斷進化、壯大,日趨完善。但直到Android 4.0問世,有關UI顯示不流暢的問題也一直未獲得根本解決。在整個進化過程當中,AndroidDisplay(顯示)系統這塊也下了很多功夫,例如,使用硬件加速等技術,但本質緣由彷佛和硬件關係並不大,由於iPhone的硬件配置並不比那些價格相近的Android機器的硬件配置強,而iPhone UI的流暢性強倒是有目共睹的。 java

Android 4.1(版本代號爲Jelly Bean)開始,Android OS開發團隊便力圖在每一個版本中解決一個重要問題(這是否是也意味着Android OS在通過幾輪大規模改善後,開始進入手術刀式的精加工階段呢?)。做爲嚴重影響Android口碑問題之一的UI流暢性差的問題,首先在Android 4.1版本中獲得了有效處理。其解決方法就是本文要介紹的Project Butter 算法

Project ButterAndroid Display系統進行了重構,引入了三個核心元素,即VSYNCTriple BufferChoreographer。其中,VSYNC是理解Project Buffer的核心。VSYNCVertical Synchronization(垂直同步)的縮寫,是一種在PC上已經很早就普遍使用的技術。讀者可簡單的把它認爲是一種定時中斷。 socket

接下來,本文將圍繞VSYNC來介紹Android Display系統的工做方式[①]。請注意,後續討論將以Display爲基準,將其劃分紅16ms長度的時間段,在每一時間段中,Display顯示一幀數據(至關於每秒60幀)。時間段從1開始編號。 函數

首先是沒有VSYNC的狀況,如圖1所示: oop

 

沒有VSYNC的繪圖過程 post

由圖1可知: 動畫

  •  時間從0開始,進入第一個16msDisplay顯示第0幀,CPU處理完第一幀後,GPU緊接其後處理繼續第一幀。三者互不干擾,一切正常。
  • 時間進入第二個16ms:由於早在上一個16ms時間內,第1幀已經由CPUGPU處理完畢。故Display能夠直接顯示第1幀。顯示沒有問題。但在本16ms期間,CPUGPU卻並未及時去繪製第2幀數據(注意前面的空白區),而是在本週期快結束時,CPU/GPU纔去處理第2幀數據。
  • 時間進入第316ms,此時Display應該顯示第2幀數據,但因爲CPUGPU尚未處理完第2幀數據,故Display只能繼續顯示第一幀的數據,結果使得第1幀多畫了一次(對應時間段上標註了一個Jank)。
  • 經過上述分析可知,此處發生Jank的關鍵問題在於,爲什麼第116ms段內,CPU/GPU沒有及時處理第2幀數據?緣由很簡單,CPU多是在忙別的事情(好比某個應用經過sleep固定時間來實現動畫的逐幀顯示),不知道該處處理UI繪製的時間了。可CPU一旦想起來要去處理第2幀數據,時間又錯過了!

爲解決這個問題,Project Buffer引入了VSYNC,這相似於時鐘中斷。結果如圖2所示: spa

引入VSYNC的繪製過程 .net

由圖2可知,每收到VSYNC中斷,CPU就開始處理各幀數據。整個過程很是完美。 線程

不過,仔細琢磨圖2卻會發現一個新問題:圖2中,CPUGPU處理數據的速度彷佛都能在16ms內完成,並且還有時間空餘,也就是說,CPU/GPUFPS(幀率,Frames Per Second)要高於DisplayFPS。確實如此。因爲CPU/GPU只在收到VSYNC時纔開始數據處理,故它們的FPS被拉低到與DisplayFPS相同。但這種處理並無什麼問題,由於Android設備的Display FPS通常是60,其對應的顯示效果很是平滑。

若是CPU/GPUFPS小於DisplayFPS,會是什麼狀況呢?請看圖3

3  CPU/GPU FPS較小的狀況

由圖3可知:

  • 在第二個16ms時間段,Display本應顯示B幀,但卻由於GPU還在處理B幀,致使A幀被重複顯示。
  • 同理,在第二個16ms時間段內,CPU無所事事,由於A BufferDisplay在使用。B BufferGPU在使用。注意,一旦過了VSYNC時間點,CPU就不能被觸發以處理繪製工做了。

爲何CPU不能在第二個16ms處開始繪製工做呢?緣由就是隻有兩個Buffer。若是有第三個Buffer的存在,CPU就能直接使用它,而不至於空閒。出於這一思路就引出了Triple Buffer。結果如圖4所示:

4  Triple Buffer的狀況

由圖4可知:

  • 第二個16ms時間段,CPU使用C Buffer繪圖。雖然仍是會多顯示A幀一次,但後續顯示就比較順暢了。

是否是Buffer越多越好呢?回答是否認的。由圖4可知,在第二個時間段內,CPU繪製的第C幀數據要到第四個16ms才能顯示,這比雙Buffer狀況多了16ms延遲。因此,Buffer最好仍是兩個,三個足矣。

介紹了上述背景知識後,下文將分析Android Project Buffer的一些細節。

  Project Buffer分析

上一節對VSYNC進行了理論分析,其實也引出了Project Buffer的三個關鍵點:

  • 核心關鍵:須要VSYNC定時中斷。
  • Triple Buffer:當雙Buffer不夠使用時,該系統可分配第三塊Buffer
  • 另外,還有一個很是隱祕的關鍵點:即將繪製工做都統一到VSYNC時間點上。這就是Choreographer的做用。Choreographer是一個極富詩意的詞,意爲舞蹈編導。在它的統一指揮下,應用的繪製工做都將變得層次分明。

下面來看Project Buffer實現的細節。

2.1  SurfaceFlinger家族的改進

首先被動刀的是SurfaceFlinger家族成員。目標是提供VSYNC中斷。相關類圖如圖5所示:

5  SurfaceFlinger中和VSYNC有關的類

由圖5可知:

  • HardwareComposer封裝了相關的HAL層,若是硬件廠商提供的HAL層實現能定時產生VSYNC中斷,則直接使用硬件的VSYNC中斷,不然HardwareComposer內部會經過VSyncThread來模擬產生VSYNC中斷(其實現很簡單,就是sleep固定時間,而後喚醒)。
  • VSYNC中斷產生時(不論是硬件產生仍是VSyncThread模擬的),VSyncHandleronVSyncReceived函數將被調用。因此,對VSYNC中斷來講,VSyncHandleronVSyncReceived,就是其中斷處理函數。

SurfaceFlinger家族中,VSyncHandler的實例是EventThread。下邊是EventThread類的聲明:

class EventThread : public Thread, public DisplayHardware::VSyncHandler

EventThread定義可知,它自己運行在一個單獨的線程中,並繼承了VSyncHandlerEventThread的核心處理在其線程函數threadLoop中完成,其處理邏輯主要是:

  • 等待下一次VSYNC的到來,並派發該中斷事件給VSYNC監聽者。

經過EventThreadVSYNC中斷事件可派發給多個該中斷的監聽者去處理。相關類如圖6所示:

6  EventThreadVSYNC中斷監聽者

由圖6可知:

  • SurfaceFlingerThread派生,其核心功能單獨運行在一個線程中。
  • SurfaceFlinger包括一個EventThread和一個MessageQueue對象。
  • 對象經過mEvents成員指向一個IDisplayEventConnection類型的對象。IDisplayEventConnection是一個純虛類,它表明VSYNC中斷的監聽者。其實體類是EventThread的內部類Connection
  • IDisplayEventConnection定義了一個getDataChannel函數,該函數返回一個BitTube實例。這個實例提供的read/write方法,用於傳送具體的信號數據(其內部實現爲socketpair,可經過Binder實現進程跨越)。

EventThread最重要的一個VSYNC監聽者就是MessageQueuemEvents對象。固然,這一切都是爲最大的後臺老闆SurfaceFlinger服務的。來自EventThreadVSYNC中斷信號,將經過MessageQueue轉化爲一個REFRESH消息並傳遞給SurfaceFlingeronMessageReceived函數處理。

有必要指出,4.1SurfaceFlinger onMessageReceived函數的實現僅僅是將4.0版本的SurfaceFlinger的核心函數挪過來罷了[②],並未作什麼改動。

以上是Project BufferSurfaceFlinger所作的一些改動。那麼Triple Buffer是怎麼處理的呢?幸虧從Android 2.2開始,DisplayPage Flip算法就不依賴Buffer的個數,Buffer個數不過是算法的一個參數罷了。因此,Triple Buffer的引入,只是把Buffer的數目改爲了3,而算法自己相對於4.0來講並無變化。圖7Triple Buffer的設置示意圖:

7  Layer.cpp中對Triple Buffer的設置

7所示,爲Layer.cpp中對Buffer個數的設置。TARGET_DISABLE_TRIPLE_BUFFERING宏可設置Buffer的個數。對某些內存/顯存並非很大的設備,也能夠選擇不使用Triple Buffer

2.2  Choreographer介紹

Choreographer是一個Java類。第一次看到這個詞時,我很激動。一個小小的命名真的反應出了設計者除coding以外的廣博的視界。試想,若是不是對舞蹈有至關了解或喜好,通常人很難想到用這個詞來描述它。

Choreographer的定義和基本結構如圖8所示:

8  Choreographer的定義和結構

8中:

  • Choreographer是線程單例的,並且必需要和一個Looper綁定,由於其內部有一個Handler須要和Looper綁定。
  • DisplayEventReceiver是一個abstract class,其JNI的代碼部分會建立一個IDisplayEventConnectionVSYNC監聽者對象。這樣,來自EventThreadVSYNC中斷信號就能夠傳遞給Choreographer對象了。由圖8可知,當VSYNC信號到來時,DisplayEventReceiveronVsync函數將被調用。
  • 另外,DisplayEventReceiver還有一個scheduleVsync函數。當應用須要繪製UI時,將首先申請一次VSYNC中斷,而後再在中斷處理的onVsync函數去進行繪製。
  • Choreographer定義了一個FrameCallback interface,每當VSYNC到來時,其doFrame函數將被調用。這個接口對Android Animation的實現起了很大的幫助做用。之前都是本身控制時間,如今終於有了固定的時間中斷。
  • Choreographer的主要功能是,當收到VSYNC信號時,去調用使用者經過postCallback設置的回調函數。目前一共定義了三種類型的回調,它們分別是:
  • CALLBACK_INPUT:優先級最高,和輸入事件處理有關。
  • CALLBACK_ANIMATION:優先級其次,和Animation的處理有關。
  • CALLBACK_TRAVERSAL:優先級最低,和UI等控件繪製有關。

優先級高低和處理順序有關。當收到VSYNC中斷時,Choreographer將首先處理INPUT類型的回調,而後是ANIMATION類型,最後纔是TRAVERSAL類型。

此外,讀者在自行閱讀Choreographer相關代碼時,還會發現AndroidMessage/Looper[③]也進行了一番小改造,使之支持Asynchronous MessageSynchronization Barrier(參照Looper.javapostSyncBarrier函數)。其實現很是巧妙,這部份內容就留給讀者本身理解並欣賞了。

相比SurfaceFlingerChoreographerAndroid 4.1中的新事物,下面將經過一個實例來簡單介紹Choreographer的工做原理。

假如UI中有一個控件invalidate了,那麼它將觸發ViewRootImplinvalidate函數,該函數將最終調用ViewRootImplscheduleTraversals。其代碼如圖9所示:

9  ViewRootImpl scheduleTraversals函數的實現

由圖9可知,scheduleTraversals首先禁止了後續的消息處理功能,這是由設置LooperpostSyncBarrier來完成的。一旦設置了SyncBarrier,全部非Asynchronous的消息便將中止派發。

而後,爲Choreographer設置了CALLBACK類型爲TRAVERSAL的處理對象,即mTraversalRunnable

最後調用scheduleConsumeBatchedInput,這個函數將爲Choreographer設置了CALLBACK類型爲INPUT的處理對象。

ChoreographerpostCallback函數將會申請一次VSYNC中斷(經過調用DisplayEventReceiverscheduleVsync實現)。當VSYNC信號到達時,Choreographer doFrame函數被調用,內部代碼會觸發回調處理。代碼片斷如圖10所示:

10  Choreographer doFrame函數片斷

ViewRootImpl來講,其TRAVERSAL回調對應的處理對象,就是前面介紹的mTraversalRunnable,它的代碼很簡單,如圖11所示:

11  mTraversalRunnable的實現

doTraversal內部實現和Android 4.0版本一致。故相比於4.0來講,4.1只是把doTraversal調用位置放到VSYNC中斷處理中了。

經過上邊的介紹,可知Choreographer確實作到了對繪製工做的統一安排,不愧是個長於統籌安排的「舞蹈編導」。

三總結

本文經過對Android Project Butter的分析,向讀者介紹了VSYNC原理以及Android Display系統的實現。除了VSYNC外,Project Butter還包括其餘一些細節的改進,例如避免重疊區域的繪製等。

簡言之,Project Butter從本質上解決了Android UI不流暢的問題,並且從Google I/O給出的視頻來看,其效果至關不錯。但實際上它對硬件配置仍是有必定要求的。由於VSYNC中斷處理的線程優先級必定要高,不然EventThread接收到VSYNC中斷,卻不能及時去處理,那就喪失同步的意義了。因此,筆者估計目前將有一大批單核甚至雙核機器沒法嚐到Jelly Bean了。

 

[①]1到圖4,請參考2012 Google I/O大會的宣傳資料

[②]關於SurfaceFlinger的代碼詳解,請參考《深刻理解Android I》第8

[③]關於MessageQueueHandler,請參考《深刻理解Android 2》第2章以及《深刻理解Android I》第5

相關文章
相關標籤/搜索