隨着時間的推移,Android OS系統一直在不斷進化、壯大,日趨完善。但直到Android 4.0問世,有關UI顯示不流暢的問題也一直未獲得根本解決。在整個進化過程當中,Android在Display(顯示)系統這塊也下了很多功夫,例如,使用硬件加速等技術,但本質緣由彷佛和硬件關係並不大,由於iPhone的硬件配置並不比那些價格相近的Android機器的硬件配置強,而iPhone UI的流暢性強倒是有目共睹的。 java
從Android 4.1(版本代號爲Jelly Bean)開始,Android OS開發團隊便力圖在每一個版本中解決一個重要問題(這是否是也意味着Android OS在通過幾輪大規模改善後,開始進入手術刀式的精加工階段呢?)。做爲嚴重影響Android口碑問題之一的UI流暢性差的問題,首先在Android 4.1版本中獲得了有效處理。其解決方法就是本文要介紹的Project Butter。 算法
Project Butter對Android Display系統進行了重構,引入了三個核心元素,即VSYNC、Triple Buffer和Choreographer。其中,VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization(垂直同步)的縮寫,是一種在PC上已經很早就普遍使用的技術。讀者可簡單的把它認爲是一種定時中斷。 socket
接下來,本文將圍繞VSYNC來介紹Android Display系統的工做方式[①]。請注意,後續討論將以Display爲基準,將其劃分紅16ms長度的時間段,在每一時間段中,Display顯示一幀數據(至關於每秒60幀)。時間段從1開始編號。 函數
首先是沒有VSYNC的狀況,如圖1所示: oop
圖1 沒有VSYNC的繪圖過程 post
由圖1可知: 動畫
爲解決這個問題,Project Buffer引入了VSYNC,這相似於時鐘中斷。結果如圖2所示: spa
圖2 引入VSYNC的繪製過程 .net
由圖2可知,每收到VSYNC中斷,CPU就開始處理各幀數據。整個過程很是完美。 線程
不過,仔細琢磨圖2卻會發現一個新問題:圖2中,CPU和GPU處理數據的速度彷佛都能在16ms內完成,並且還有時間空餘,也就是說,CPU/GPU的FPS(幀率,Frames Per Second)要高於Display的FPS。確實如此。因爲CPU/GPU只在收到VSYNC時纔開始數據處理,故它們的FPS被拉低到與Display的FPS相同。但這種處理並無什麼問題,由於Android設備的Display FPS通常是60,其對應的顯示效果很是平滑。
若是CPU/GPU的FPS小於Display的FPS,會是什麼狀況呢?請看圖3:
圖3 CPU/GPU FPS較小的狀況
由圖3可知:
爲何CPU不能在第二個16ms處開始繪製工做呢?緣由就是隻有兩個Buffer。若是有第三個Buffer的存在,CPU就能直接使用它,而不至於空閒。出於這一思路就引出了Triple Buffer。結果如圖4所示:
圖4 Triple Buffer的狀況
由圖4可知:
是否是Buffer越多越好呢?回答是否認的。由圖4可知,在第二個時間段內,CPU繪製的第C幀數據要到第四個16ms才能顯示,這比雙Buffer狀況多了16ms延遲。因此,Buffer最好仍是兩個,三個足矣。
介紹了上述背景知識後,下文將分析Android Project Buffer的一些細節。
上一節對VSYNC進行了理論分析,其實也引出了Project Buffer的三個關鍵點:
下面來看Project Buffer實現的細節。
首先被動刀的是SurfaceFlinger家族成員。目標是提供VSYNC中斷。相關類圖如圖5所示:
圖5 SurfaceFlinger中和VSYNC有關的類
由圖5可知:
在SurfaceFlinger家族中,VSyncHandler的實例是EventThread。下邊是EventThread類的聲明:
class EventThread : public Thread, public DisplayHardware::VSyncHandler
由EventThread定義可知,它自己運行在一個單獨的線程中,並繼承了VSyncHandler。EventThread的核心處理在其線程函數threadLoop中完成,其處理邏輯主要是:
經過EventThread,VSYNC中斷事件可派發給多個該中斷的監聽者去處理。相關類如圖6所示:
圖6 EventThread和VSYNC中斷監聽者
由圖6可知:
EventThread最重要的一個VSYNC監聽者就是MessageQueue的mEvents對象。固然,這一切都是爲最大的後臺老闆SurfaceFlinger服務的。來自EventThread的VSYNC中斷信號,將經過MessageQueue轉化爲一個REFRESH消息並傳遞給SurfaceFlinger的onMessageReceived函數處理。
有必要指出,4.1中SurfaceFlinger onMessageReceived函數的實現僅僅是將4.0版本的SurfaceFlinger的核心函數挪過來罷了[②],並未作什麼改動。
以上是Project Buffer對SurfaceFlinger所作的一些改動。那麼Triple Buffer是怎麼處理的呢?幸虧從Android 2.2開始,Display的Page Flip算法就不依賴Buffer的個數,Buffer個數不過是算法的一個參數罷了。因此,Triple Buffer的引入,只是把Buffer的數目改爲了3,而算法自己相對於4.0來講並無變化。圖7爲Triple Buffer的設置示意圖:
圖7 Layer.cpp中對Triple Buffer的設置
圖7所示,爲Layer.cpp中對Buffer個數的設置。TARGET_DISABLE_TRIPLE_BUFFERING宏可設置Buffer的個數。對某些內存/顯存並非很大的設備,也能夠選擇不使用Triple Buffer。
Choreographer是一個Java類。第一次看到這個詞時,我很激動。一個小小的命名真的反應出了設計者除coding以外的廣博的視界。試想,若是不是對舞蹈有至關了解或喜好,通常人很難想到用這個詞來描述它。
Choreographer的定義和基本結構如圖8所示:
圖8 Choreographer的定義和結構
圖8中:
優先級高低和處理順序有關。當收到VSYNC中斷時,Choreographer將首先處理INPUT類型的回調,而後是ANIMATION類型,最後纔是TRAVERSAL類型。
此外,讀者在自行閱讀Choreographer相關代碼時,還會發現Android對Message/Looper類[③]也進行了一番小改造,使之支持Asynchronous Message和Synchronization Barrier(參照Looper.java的postSyncBarrier函數)。其實現很是巧妙,這部份內容就留給讀者本身理解並欣賞了。
相比SurfaceFlinger,Choreographer是Android 4.1中的新事物,下面將經過一個實例來簡單介紹Choreographer的工做原理。
假如UI中有一個控件invalidate了,那麼它將觸發ViewRootImpl的invalidate函數,該函數將最終調用ViewRootImpl的scheduleTraversals。其代碼如圖9所示:
圖9 ViewRootImpl scheduleTraversals函數的實現
由圖9可知,scheduleTraversals首先禁止了後續的消息處理功能,這是由設置Looper的postSyncBarrier來完成的。一旦設置了SyncBarrier,全部非Asynchronous的消息便將中止派發。
而後,爲Choreographer設置了CALLBACK類型爲TRAVERSAL的處理對象,即mTraversalRunnable。
最後調用scheduleConsumeBatchedInput,這個函數將爲Choreographer設置了CALLBACK類型爲INPUT的處理對象。
Choreographer的postCallback函數將會申請一次VSYNC中斷(經過調用DisplayEventReceiver的scheduleVsync實現)。當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了。