SurfaceFlinger服務(wù)負責繪制Android應(yīng)用程序的UI,它的實現(xiàn)相當復(fù)雜,要從正面分析它的實現(xiàn)不是一件容易的事。既然不能從正面分析,我們就想辦法從側(cè)面分析。說到底,無論SurfaceFlinger服務(wù)有多復(fù)雜,它都是為Android應(yīng)用程序服務(wù)的,因此,我們就從Android應(yīng)用程序與SurfaceFlinger服務(wù)的關(guān)系入手,來概述和制定SurfaceFlinger服務(wù)的學習計劃。
SurfaceFlinger服務(wù)運行在Android系統(tǒng)的System進程中,它負責管理Android系統(tǒng)的幀緩沖區(qū)(Frame Buffer)。Android系統(tǒng)的幀緩沖區(qū)的相關(guān)知識,可以參考前面兩篇文章Android系統(tǒng)的開機畫面顯示過程分析和Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析。Android應(yīng)用程序為了能夠?qū)⒆约旱腢I繪制在系統(tǒng)的幀緩沖區(qū)上,它們就必須要與SurfaceFlinger服務(wù)進行通信,如圖1所示:
圖1 Android應(yīng)用程序與SurfaceFlinger服務(wù)的關(guān)系
注意,Android應(yīng)用程序與SurfaceFlinger服務(wù)是運行在不同的進程中的,因此,它們采用Binder進程間通信機制來進行通信。Android系統(tǒng)的Binder進程間通信機制的相關(guān)知識,可以參考Android進程間通信(IPC)機制Binder簡要介紹和學習計劃這一系列的文章。
在圖1中,每一個Android應(yīng)用程序與SurfaceFlinger服務(wù)都有一個連接,這個連接都是通過一個類型為Client的Binder對象來描述的。這些Client對象是Android應(yīng)用程序連接到SurfaceFlinger服務(wù)的時候由SurfaceFlinger服務(wù)創(chuàng)建的,而當Android應(yīng)用程序成功連接到SurfaceFlinger服務(wù)之后,就可以獲得一個對應(yīng)的Client對象的Binder代理接口了。有了這些Binder代理接口之后,Android應(yīng)用程序就可以通知SurfaceFlinger服務(wù)來繪制自己的UI了。
Android應(yīng)用程序在通知SurfaceFlinger服務(wù)來繪制自己的UI的時候,需要將UI元數(shù)據(jù)傳遞給SurfaceFlinger服務(wù),例如,要繪制UI的區(qū)域、位置等信息。一個Android應(yīng)用程序可能會有很多個窗口,而每一個窗口都有自己的UI元數(shù)據(jù),因此,Android應(yīng)用程序需要傳遞給SurfaceFlinger服務(wù)的UI元數(shù)據(jù)是相當可觀的。在這種情況下,通過Binder進程間通信機制來在Android應(yīng)用程序與SurfaceFlinger服務(wù)之間傳遞UI元數(shù)據(jù)是不合適的,這時候Android系統(tǒng)的匿名共享內(nèi)存機制(Anonymous Shared Memory)就派上用場了。Android系統(tǒng)的匿名共享內(nèi)存機制的相關(guān)知識,可以參考Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃這一系列的文章。
在每一個Android應(yīng)用程序與SurfaceFlinger服務(wù)之間的連接上加上一塊用來傳遞UI元數(shù)據(jù)的匿名共享內(nèi)存,我們就得到了圖2,如下所示:
圖2 用來在Android應(yīng)用程序與SurfaceFlinger服務(wù)之間傳遞UI元數(shù)據(jù)的匿名共享內(nèi)存
在Application和Client這兩個高富帥看來,它們之間的原生匿名共享內(nèi)存塊就一個活脫脫的土肥圓。因此,Application和Client是看不上這塊原生的匿名共享內(nèi)存的。于是,這塊原生的匿名共享內(nèi)存當時就怒了,立志要逆襲變成白富美,如圖3所示:
圖3 結(jié)構(gòu)化后的用來傳遞UI元數(shù)據(jù)的匿名共享內(nèi)存塊
土肥圓逆襲后,就變成了一個名字為SharedClient的白富美,從此,它就和Application、Client過上幸福的啪啪啪生活了。
SharedClient到底有多白多富多美?參見圖4:
圖4 用來描述Android應(yīng)用程序的UI元數(shù)據(jù)的SharedClient
在每一個SharedClient里面,有至多31個SharedBufferStack。字面上來看,SharedBufferStack就是共享緩沖區(qū)堆棧。怎么理解呢?首先,Shared表明這個堆棧共享的。那么由誰來共享呢?當然就是Android應(yīng)用程序和SurfaceFlinger服務(wù)了。其次,Buffer表明這個堆棧的內(nèi)容是緩沖區(qū)。什么樣的緩沖區(qū)呢?當然就是用來描述UI元數(shù)據(jù)的緩沖區(qū)了。再者,Stack表明用來描述UI元數(shù)據(jù)的緩沖區(qū)是需要按照一定的規(guī)則來訪問的。綜合起來,我們就可以認為每一個SharedBufferStack就是用來描述一系列需要按照一定規(guī)則來訪問的緩沖區(qū)。
好像還是不能理解SharedBufferStack?好吧,回憶一下,一般我們就繪制UI的時候,都會采用一種稱為“雙緩沖”的技術(shù)。雙緩沖意味著要使用兩個緩沖區(qū),其中一個稱為Front Buffer,另外一個稱為Back Buffer。UI總是先在Back Buffer中繪制,然后再和Front Buffer交換,渲染到顯示設(shè)備中。這下就可以理解SharedBufferStack的含義了吧?SurfaceFlinger服務(wù)只不過是將傳統(tǒng)的“雙緩沖”技術(shù)升華和抽象為了一個SharedBufferStack。可別小看了這個升華和抽象,有了SharedBufferStack之后,SurfaceFlinger服務(wù)就可以使用N個緩沖區(qū)技術(shù)來繪制UI了。N值的取值范圍為2到16。例如,在Android 2.3中,N的值等于2,而在Android 4.1中,據(jù)說就等于3了。
我們還可以再進一步地理解SharedBufferStack。在SurfaceFlinger服務(wù)中,每一個SharedBufferStack都對應(yīng)一個Surface,即一個窗口。這樣,我們就可以知道為什么每一個SharedClient里面包含的是一系列SharedBufferStack而不是單個SharedBufferStack:一個SharedClient對應(yīng)一個Android應(yīng)用程序,而一個Android應(yīng)用程序可能包含有多個窗口,即Surface。從這里也可以看出,一個Android應(yīng)用程序至多可以包含31個Surface。
SharedBufferStack長什么樣子呢?看圖5:
圖 5 SharedBufferStack的結(jié)構(gòu)示意圖
在圖5中,為了方便描述,我們假設(shè)圖中的SharedBufferStack有5個Buffer,其中,Buffer-1和Buffer-2是已經(jīng)使用了的,而Buffer-3、Buffer-4和Buffer-5是空閑的。指針head和tail分別指向空閑緩沖區(qū)列表的頭部和尾部,而指針queue_head指向已經(jīng)使用了的緩沖區(qū)列表的頭部。從這里就可以看出,從指針tail到head之間的Buffer即為空閑緩沖區(qū)表,而從指針head到queue_head之間的Buffer即為已經(jīng)使用了的緩沖區(qū)列表。注意,圖中的5個Buffer是循環(huán)使用的。
空閑緩沖區(qū)比較好理解,接下來我們重點解釋一下那些已經(jīng)被使用了的緩沖區(qū),即圖5中的Buffer-1和Buffer-2。
前面我們說過,SharedBufferStack中的緩沖區(qū)只是用來描述UI元數(shù)據(jù)的,這意味著它們不包含真正的UI數(shù)據(jù)。真正的UI數(shù)據(jù)保存在GraphicBuffer中,后面我們再描述GaphicBuffer。因此,為了完整地描述一個UI,SharedBufferStack中的每一個已經(jīng)使用了的緩沖區(qū)都對應(yīng)有一個GraphicBuffer,用來描述真正的UI數(shù)據(jù)。當SurfaceFlinger服務(wù)緩制Buffer-1和Buffer-2的時候,就會找到與它們所對應(yīng)的GraphicBuffer,這樣就可以將對應(yīng)的UI繪制出來了。
當Android應(yīng)用程序需要更新一個Surface的時候,它就會找到與它所對應(yīng)的SharedBufferStack,并且從它的空閑緩沖區(qū)列表的尾部取出一個空閑的Buffer。我們假設(shè)這個取出來的空閑Buffer的編號為index。接下來Android應(yīng)用程序就請求SurfaceFlinger服務(wù)為這個編號為index的Buffer分配一個圖形緩沖區(qū)GraphicBuffer。SurfaceFlinger服務(wù)分配好圖形緩沖區(qū)GraphicBuffer之后,會將它的編號設(shè)置為index,然后再將這個圖形緩沖區(qū)GraphicBuffer返回給Android應(yīng)用程序訪問。Android應(yīng)用程序得到了SurfaceFlinger服務(wù)返回的圖形緩沖區(qū)GraphicBuffer之后,就在里面寫入UI數(shù)據(jù)。寫完之后,就將與它所對應(yīng)的緩沖區(qū),即編號為index的Buffer,插入到對應(yīng)的SharedBufferStack的已經(jīng)使用了的緩沖區(qū)列表的頭部去。這一步完成了之后,Android應(yīng)用程序就通知SurfaceFlinger服務(wù)去繪制那些保存在已經(jīng)使用了的緩沖區(qū)所描述的圖形緩沖區(qū)GraphicBuffer了。用圖5的例子來說,SurfaceFlinger服務(wù)需要繪制的是編號為1和2的Buffer所對應(yīng)的圖形緩沖區(qū)GraphicBuffer。由于SurfaceFlinger服務(wù)知道編號為1和2的Buffer所對應(yīng)的圖形緩沖區(qū)GraphicBuffer在哪里,因此,Android應(yīng)用程序只需要告訴SurfaceFlinger服務(wù)要繪制的Buffer的編號就OK了。當一個已經(jīng)被使用了的Buffer被繪制了之后,它就重新變成一個空閑的Buffer了。
上面描述的過程比較復(fù)雜,后面我們再用幾篇文章來詳細描述。
SharedBufferStack是在Android應(yīng)用程序和SurfaceFlinger服務(wù)之間共享的,但是,Android應(yīng)用程序和SurfaceFlinger服務(wù)使用SharedBufferStack的方式是不一樣的,具體來說,就是Android應(yīng)用程序關(guān)心的是它里面的空閑緩沖區(qū)列表,而SurfaceFlinger服務(wù)關(guān)心的是它里面的已經(jīng)使用了的緩沖區(qū)列表。從SurfaceFlinger服務(wù)的角度來看,保存在SharedBufferStack中的已經(jīng)使用了的緩沖區(qū)其實就是在排隊等待渲染。
為了方便SharedBufferStack在Android應(yīng)用程序和SurfaceFlinger服務(wù)中的訪問,Android系統(tǒng)分別使用SharedBufferClient和SharedBufferServer來描述SharedBufferStack,其中,SharedBufferClient用來在Android應(yīng)用程序這一側(cè)訪問SharedBufferStack的空閑緩沖區(qū)列表,而SharedBufferServer用來在SurfaceFlinger服務(wù)這一側(cè)訪問SharedBufferStack的排隊緩沖區(qū)列表。
在SharedBufferClient看來,SharedBufferStack的樣子如圖6所示:
圖6 SharedBufferClient眼中的SharedBufferStack
只要SharedBufferStack中的available的buffer的數(shù)量大于0,SharedBufferClient就會將指針tail往前移一步,并且減少available的值,以便可以獲得一個空閑的Buffer。當Android應(yīng)用程序往這個空閑的Buffer寫入好數(shù)據(jù)之后,它就會通過SharedBufferClient來將它添加到SharedBufferStack中的排隊緩沖區(qū)緩沖區(qū)列表的尾部去,即指針queue_head的下一個位置上。
在SharedBufferServer看來,SharedBufferStack的樣子如圖7所示:
圖7 SharedBufferServer眼中的SharedBufferStack
當Android應(yīng)用程序通知SurfaceFlinger服務(wù)更新UI的時候,只要對應(yīng)的SharedBufferStack中的queued的緩沖區(qū)的數(shù)量大于0,SharedBufferServer就會將指針head的下一個Buffer繪制出來,并且將指針head向前移一步,以及將queued的值減1。
上面我們多次提到了圖形緩沖區(qū)GraphicBuffer,它是什么東東呢?我們看圖8:
圖8 圖形緩沖區(qū)Graphic的結(jié)構(gòu)示意圖
每一個GraphicBuffer內(nèi)部都包含有一塊用來保存UI數(shù)據(jù)的緩沖區(qū),這塊緩沖區(qū)使用一個buffer_handle_t對象來描述。看到buffer_handle_t,是不是有點眼熟?在前面Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析一文中,我們說過,由HAL層的Gralloc模塊分配的圖形緩沖區(qū)的是使用一個buffer_handle_t對象來描述的,而由buffer_handle_t對象所描述的圖形緩沖區(qū)要么是在系統(tǒng)幀緩沖區(qū)(Frame Buffer)或者匿名共享內(nèi)存(Anonymous Shared Memory)中分配的。這樣,我們就可以將SurfaceFlinger服務(wù)與HAL層中的Gralloc模塊關(guān)聯(lián)起來了。
至此,ndroid應(yīng)用程序與SurfaceFlinger服務(wù)的關(guān)系就概述完畢了,但是我們的任務(wù)還沒有完成,我們還要進一步去具體地學習它,例如:
1. Android應(yīng)用程序是如何與SurfaceFlinger服務(wù)建立連接的?
2. 用來描述Android應(yīng)用程序的UI元數(shù)據(jù)的SharedClient是如何創(chuàng)建的?
3. Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù)創(chuàng)建一個Surface的?
4. Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù)渲染一個Surface的?
回答了這4個問題之后,相信我們就可以對SurfaceFlinger服務(wù)有一個深刻的認識,進而可以幫助我們從正面去分析SurfaceFlinger服務(wù)的實現(xiàn)。后面我們將以Android系統(tǒng)的開機動畫為例子,用4篇文章來回答這4個問題,敬請關(guān)注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!