Android 4.4(KitKat)中的設計模式-Graphics子系統

本文主要從設計模式角度簡單地侃下Android4.4(KitKat)的Graphics子系統。能夠看到在KitKat中Google對code仍是整理過的,好比替換了像SurfaceTexture這種第一眼看到不知所云的東西,去掉了像ISurface這種打醬油的定義,改掉了明明是SurfaceHolder類型卻死皮白臉叫surface的變量。自從修正了這些晦澀逆天的概念後,媽媽不再用擔憂我看不懂Android的code了。固然仍然有很多legacy code,若是沒看過之前版本的話會有些小迷茫,好在無傷大雅。接下來言歸正傳。做爲一個操做系統,Android須要考慮到靈活性,兼容性,可用性,可維護性等方方面面 ,爲了達到這些需求,它須要良好的設計。所以,在Android源碼中能夠看到不少設計模式的身影。光是本文涉及的Graphics子系統中,就用到了如Observer, Proxy, Singleton, Command, Decorator, Strategy, Adapter, Iterator和Simple Factory等模式。若是要學習設計模式,我想Android源代碼是一個比較好的實例教材。固然不少用法和GoF書中的經典示例不同,但其理念仍是值得學習的。本文仍以大致流程爲綱,在涉及到時穿插相應的設計模式。這樣既涵蓋了Android工做原理,又能一窺其設計理念,一箭雙鵰。這裏本意是想自頂向下地展開,由於Android源代碼龐大,很容易迷失在code的海洋中。本着divide-and-conquer的原則,本文重點先介紹SurfaceFlinger也就是服務端的工做流程,而對於應用程序端的App UI部分留到之後再講。 android

 

爲了讓分析不過於抽象,首先,讓咱們先找一個能夠跑的實例爲起點,使得分析更加有血有肉。這裏選的是/frameworks/native/services/surfaceflinger/tests/resize/resize.cpp。選這個測試用例緣由有幾:1、它是一個Native程序,不牽扯它們在Java層的那一坨代理和Jni調用。2、麻雀雖小,五臟俱全,應用程序在Native層的主幹它都有。3、程序無廢話,簡約而不簡單,高端洋氣上檔次。注意這個用例默認編譯有錯的,不過好在對於碼農們不是大問題,改發改發也就過了。

下面是該用例中最核心的幾行,咱們來看看這樣簡單的任務後面Android到底作了神馬。 編程

sp<surfacecomposerclient> client = new SurfaceComposerClient();

    sp<surfacecontrol> surfaceControl = client->createSurface(String8(resize),
            160, 240, PIXEL_FORMAT_RGB_565, 0);

    sp<surface> surface = surfaceControl->getSurface();

    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setLayer(100000);
    SurfaceComposerClient::closeGlobalTransaction();

    ANativeWindow_Buffer outBuffer;
    surface->lock(&outBuffer, NULL);
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
    surface->unlockAndPost();</surface></surfacecontrol></surfacecomposerclient>

這兒的大概流程是先建立SurfaceComposerClient,再經過它建立SurfaceControl,再獲得Surface,而後經過lock()分配圖形緩衝區,接着把要渲染的東西(這裏是簡單的紅色)畫好後,用unlockAndPost()交給SurfaceFlinger去放到硬件緩衝區中,也就是畫到屏幕上。最後結果是屏幕上應該能看到一個小紅色塊。這裏先不急着挖代碼,先放慢腳步直觀地理解下這些概念。SurfaceComposer能夠理解爲SurfaceFlinger的別稱,由於SurfaceFlinger主要就是用來作各個圖層的Composition操做的。但SurfaceFlinger是在服務端的,做爲應用程序要讓它爲之服務須要先生成它所對應的客戶端,也就是SurfaceComposerClient,由於SurfaceComposerClient是懂得和服務端打交道的協議的。SurfaceComposerClient位於服務端的代理叫Client,應用程序經過前者發出的遠程過程調用就是經過後者來實現的。有了它以後,應用程序就能夠經過它來申請SurfaceFlinger爲本身建立Surface,這個Surface就是要繪製的表面的抽象。去除次要元素,下圖簡要勾勒了這些類之間的整體結構: 設計模式

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095102172.jpg

把這些個結構畫出來後,優美而對稱的C/S架構就躍然電腦上了。中間爲接口層,兩邊的服務端和客戶端(也就是應用程序)經過接口就能夠相互通訊。這樣兩邊均可以作到設計中的「面向接口編程」。只要接口不變,兩邊實現怎麼折騰都行。能夠看到,應用程序要經過SurfaceFlinger將本身的內容渲染到屏幕上是一個客戶端請求服務的過程。其中應用程序端爲生產者,提供要渲染的圖形緩衝區,SurfaceFlinger爲消費者,拿圖形緩衝區去合併渲染。從結構上來看,能夠看到應用程序端的東西在服務端都有對應的對象。注意它們的生成順序是由上到下的,即對於應用程序來講,先有ComposerService,再有SurfaceComposerClient,再有Surface;對於服務端來講,依次有SurfaceFlinger,Client和Layer。 數組

 

這裏至少看到兩種設計模式 :Singleton和Proxy模式。固然用法可能和教科書中不同,可是思想是一致的。ComposerService用來抽象SurfaceFlinger,SurfaceFlinger全局只有一個,因此ComposerService是Singleton對象。另外一方面,應用程序想要讓服務端爲其作事,但服務端不在同一進程中,這就須要在服務端建立本地對象在服務端的代理(如Client),這就是Proxy模式了。Proxy模式應用很廣,可用於遠程對象訪問(remote proxy),虛擬化(virtual proxy),權限控制(protection proxy)和智能指針(smart reference proxy)等。這裏用的是remote proxy(固然也有protection proxy的因素)。另外smart reference proxy模式的例子如Android中的智能指針。 架構

 

大致結構講完,下面分步講流程。首先,建立SurfaceComposerClient的流程見下面的序列圖: app

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095103173.jpg

沒啥亮點,一筆帶過。前戲結束,下面進入正題,應用程序建立Surface過程以下: composer

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095103174.jpg

挺直觀的過程,細節從略。要注意的有幾點: 框架

1、客戶端到服務端的調用都是經過Binder來進行的。若是不知道Binder是什麼也不要緊,只要把它看做一個面向系統級的IPC機制就行。上面在客戶端和服務端之間不少的進程間過程調用就是用它來完成的。 異步

2、現實中,像調用SurfaceFlinger的createLayer()函數這種並不那麼直接,而是採用異步調用方式。這個一會再講。 socket

3、IGraphicBufferProducer,IGraphicBufferConsumer, BufferQueue, SurfaceTextureLayer這幾個類是一脈相承的。因此圖中當服務端建立一個SurfaceTextureLayer對象,傳到客戶端被轉成IGraphicBufferProducer是徹底OK的。這種面向接口編程的用法還有不少,其理論基礎是里氏替換原則。這裏也體現了接口隔離原則,一樣的對象在客戶端只暴露做爲生產者的接口,而在服務端暴露消費者的接口,避免了接口污染。

 

那麼上面的過程當中用到了哪些設計模式呢?我以爲比較明顯的有如下幾個:

 

Proxy模式這裏被用來解除環形引用和避免對象被回收。ConsumerBase先建立一個類型爲BufferQueue::ConsumerListerner的對象listerner,而後再在外面包了層類型爲QueueBuffer::ProxyConsumerListener的代理對象proxy,它擁有一個指向listerner的弱指針(因弱引用不影響回收)。一方面,加了這一層代理至關於把相互的強引用變成了單向的強引用,之因此這樣作是爲了不對象間的環形引用致使難以回收。另外一方面,若是用的強指針,那在ConsumerBase構造函數中,一旦函數結束,對於ConsumerListener(即ConsumerBase)的強引用就沒有了,那麼onLastStrongRef()就可能被調用,從而回收這個還有用的對象。前面咱們看到Proxy模式在遠程對象訪問中的應用,這裏咱們看到了另外一種用法。

 

Observer模式被用作在應用程序渲染完後提交給服務端時的通知機制。首先consumerConnect()會創建起BufferQueue到ConsumerBase對象的引用,放於成員變量mConsumerListener之中,接着setFrameAvailableListener()創建起ConsumerBase對象到Layer的引用。這樣就造成了下面這樣一個鏈式Observer模式:

 

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095103175.jpg

注意雖是鏈式,但和Chain of Responsiblity模式沒啥關係。註冊完成以後,往後當應用程序處理完圖形緩衝區後調用queueBuffer()時,BufferQueue就會調用這個listener的回調函數onFrameAvailable(),而後途經ConsumerBase調用Layer的回調函數onFrameAvailable(),最後調用signalLayerUpdate()使SurfaceFlinger獲得通知。這樣就使得SurfaceFlinger能夠專心幹本身的事,當有需求來時天然會被通知到。事實上,當被通知有新幀須要渲染時,SurfaceFlinger也不是立刻停下手上的事,而是先作一個輕量級的處理,也就是把相應消息放入消息隊列留到之後處理。這就使得各個應用程序和SurfaceFlinger能夠異步工做,保證SurfaceFlinger的性能不被影響。而這又引入了下面的Command模式。

 

Command模式被用來使得SurfaceFlinger能夠和其它模塊異步工做。Command模式常被用來實現undo操做或是延遲處理,這裏顯然是用了後者。簡單地歸納下就是:當有消息來(如INVALIDATE消息),先把它經過MessageQueue存起來(實際是存在Looper裏),而後SurfaceFlinger線程會不斷去查詢這個消息隊列。若是隊列不爲空且約定處理時間已到,就會取出作相應處理。具體流程後面談到SurfaceFlinger時再說。爲何要這麼麻煩呢,主要仍是要保證SurfaceFlinger的穩定和高效。消息能夠隨時過來,但SurfaceFlinger能夠異步處理。這樣就能夠保證不打亂SurfaceFlinger那最搖擺的節奏,從而保證用戶體驗。

 

建立不一樣功能的BufferQueue使用的是相似於Builder的設計模式。當BufferQueue被建立出來後,它擁有默認的參數,客戶端爲把它打形成想要的BufferQueue,經過其接口設定一系列的參數便可,就像Wizard同樣。爲何要這麼作呢,先了解下背景知識。一個圖形緩衝區從應用程序到屏幕的路上兩個地方用到了BufferQueue。一個用來將應用程序渲染好的圖形緩衝傳給SurfaceFlinger,另外一個用來把SurfaceFlinger合成好的圖形緩衝放到硬件圖形緩衝區上。

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095103176.jpg

BufferQueue中核心數據是一個GraphicBuffer的隊列。而GraphicBuffer根據使用場合的不一樣能夠從共享內存(即Ashmem,由於這塊內存要在應用程序和服務端程序兩個進程間共享)或者從硬件圖形緩衝區(即Framebuffer,由於它是SurfaceFlinger渲染完要放到屏幕上的)中分配。另外由於用途不一樣,它的格式,大小,以及在BufferQueue中的數量均可能是不一樣的。雖然是同一個類,用於不一樣場合出身就不一樣,那又怎麼區分哪一個是高富帥,哪一個是矮窮挫呢。很簡單,當BufferQueue被建立出來以後,由Layer或是FramebufferSurface來充當導演的角色,打造相應的BufferQueue。它們調用一系列的函數(如setConsumerUsageBits()和setDefaultMaxBufferCount()等)將構建出來的BufferQueue變得適用於本身。

 

另外,Adapter和Decorator模式在代碼中也常常會出現。從目的上講,因爲被調用者提供的接口或功能經常不能知足調用者的需求,若是是接口不知足就用Adapter模式,若是要增長額外功能就用Decorator模式。從結構上講,Adapter能夠是Subclassing結構也能夠是Composition結構,而Decorator通常是Composition結構。事實上這兩個模式常常混在一塊兒用。實用中我以爲不必太在乎究竟是什麼模式,能起到做用就行。舉例來講,SurfaceFlingerConsumer是GLConsumer的Wrapper,當Layer調用SurfaceFlingerConsumer的接口,底層會部分使用GLConsumer的相應實現(事實上SurfaceFlingerConsumer和GLConsumer實現中有重複代碼)。我以爲它是用了subclassing結構來達到了相似Decorator模式的目的。固然這裏模式用得不是很清晰,只是藉機引下相關模式,例子跳過也罷。

 

回到咱們的測試用例主線上,如今應用程序中Surface建立好了,下面幾行主要功能是把應用程序所繪圖層的z軸值設得很大,也就是很牛逼確定能看到的地方。

SurfaceComposerClient::openGlobalTransaction();
   surfaceControl->setLayer(100000);
   SurfaceComposerClient::closeGlobalTransaction(); 

像這種更改屏幕或是應用程序窗口屬性的動做須要用openGlobalTransaction()和closeGlobalTransaction()包起來,這樣中間的更改操做就成爲一個事務。事務中的操做暫時只在本地,只有當closeGlobalTransaction()被調用時才一塊兒經過Binder發給SurfaceFlinger處理。這主要是由另外一個Singleton對象Compoesr來實現的。屬性改變的事務化優化了系統資源,由於更改這些屬性的操做每每很heavy,意味着不少東西須要從新計算,因此這裏把這些費時操做打了一個包,避免重複勞動。因爲這塊並不複雜也不是重點,並且部分流程和後面重複,因此就跳過直接進入高潮 - 寫圖形緩衝區和交由SurfaceFlinger合成輸出到屏幕。讓咱們看看下面這幾行背後都作了些什麼:

ANativeWindow_Buffer outBuffer;
   surface->lock(&outBuffer, NULL);
   ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
   android_memset16((uint16_t*)outBuffer.bits,0xF800, bpr*outBuffer.height);
   surface->unlockAndPost();

代碼首先定義了圖形緩衝區,它就是應用程序用來畫圖的緩衝區了。但這裏只有元信息,放繪製數據的地方還沒分配出來。先看下面這兩行繪製緩衝區的語句,它們的目的很簡單,就是把圖形緩衝區整成紅色。注意Surface格式是PIXEL_FORMAT_RGB_565,因此紅色對應0xF800。

ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
   android_memset16((uint16_t*)outBuffer.bits,0xF800, bpr*outBuffer.height); 

繪製緩衝區先後分別用lock()和unlockAndPost()包起來,這兩個函數主要用途是向服務端申請圖形緩衝區,再把繪製好的圖形緩衝區交給SurfaceFlinger處理。大致流程以下:

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095104177.jpg

這樣應用程序就把本身渲染好的圖形緩衝區華麗麗地交給SurfaceFlinger了。其中最主要的是圖形緩衝區的傳遞處理,圖形緩衝區對應的類爲GraphicBuffer。BufferQueue爲它的緩衝隊列,能夠看做是一個元素爲GraphicBuffer的隊列。插播下背景知識,BufferQueue中的GraphicBuffer有如下幾種狀態,之間的轉換關係以下:

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095104178.jpg

 

這部分用到的設計模式主要有如下幾個:

 

Memento模式。從BufferQueue傳回的GraphicBuffer是在共享內存中分配的,而這塊內存又是用句柄來表示的。咱們知道一個進程的句柄或地址到另外一個進程中就不必定合法了。那麼爲何Surface的lock()函數拿到這個GraphicBuffer後就直接拿來當本地對象用了呢。這裏就要引入Memento模式了。GraphicBuffer繼承了Flattenable類,實現了flatten()和unflatten函數。當GraphicBuffer被從進程A經由Binder傳遞到進程B時,先在進程A中它的flatten()函數被調用,再在進程B中unflatten()函數被調用。這樣就能夠保證GraphicBuffer跨進程後仍然有效。簡要地看下flatten()和unflatten()的實現。服務端在傳遞GraphicBuffer前調用flatten()函數把GraphicBuffer的基本信息存在結構體中,應用程序端拿到後調用unflatten()將這個結構體拿出來後調用GraphicBufferMapper::registerBuffer()(它接着會調用gralloc模塊中的gralloc_register_buffer(),接着調用gralloc_map(),最後用mmap()完成映射)將遠端GraphicBuffer映射到本地地址空間,而後據此在BpGraphicBufferProducer::requestBuffer()中從新構建了一個GraphicBuffer。這樣應用程序端和服務端中的GraphicBuffer就被映射到了同一塊物理空間,達到了共享的目的,並且對於上層應用這個轉化的過程徹底是透明的。QueueBufferOutput和QueueBufferInput的處理與GraphicBuffer的處理目的相同,都是進程間傳遞對象,不一樣之處在於前二者至關於在另外一個進程中拷貝了一份。由於它們只包含少許POD成員,因此拷貝對性能影響不大。

 

Iterator模式在Android源碼中也很散落地應用着。如Surface::lock()中,爲了使得frontBuffer能夠重用(圖形渲染通常採用雙緩衝,frontBuffer用於顯示輸出,backBuffer用於做圖,這樣二者能夠同時進行互不影響。不少時候其實下一幀和前一幀比只有一小塊是須要從新渲染的,如切水果時不少時候其實就水果周圍一塊區域須要重渲染,這塊區域即爲髒區域),咱們須要知道髒區域。髒區域信息用Region表示。而Region自己是由多個矩形組成的,而做爲客戶端要訪問Region中的這些矩形,不須要知道它們的內在實現。這樣在訪問這個髒區域時就能夠這麼寫:

Region::const_iterator head(reg.begin());
Region::const_iterator tail(reg.end());
while(head != tail) {
    …
 
} 

這樣客戶端的代碼就不依賴於Region的實現了,不管Region中是數組也好,隊列也好,上層代碼不變,無比優美。同理還有BufferQueue::queueBuffer()中用到的Fifo::iterator等。

 

Strategy模式用於讓系統能夠優雅地適應不一樣平臺中的HAL模塊。儘管這兒部分代碼是用C寫的,和標準的Strategy用法略有不一樣,但精神是同樣的。舉例來講,因爲gralloc模塊是平臺相關的,在不一樣平臺有不一樣的實現。那麼做爲上層客戶端,如GraphicBufferMapper來講,它不但願依賴於這種變化。因而要求全部平臺提供的gralloc模塊提供統一的接口,而這個接口對應的對象的符號統一爲HAL_MODULE_INFO_SYM。這樣無論是哪一個平臺,上層客戶端就只要用dlopen()和dlsym()去載入和查詢這個結構體就能夠獲得這些接口相應的實現了(載入流程見hw_get_module() ->hw_get_module_by_class() -> load())。這裏涉及到的是gralloc_module_t接口,也就是GraphicBufferMapper要用到的接口,各個平臺提供的gralloc都要實現這個接口。同理的還有對同在HAL層的hwcomposer模塊的處理。

 

另外,咱們還看到了前面提到過的設計模式又一次次地出現,如死亡通知是基於Binder的Observer模式,另外GraphicBufferMapper對gralloc模塊gralloc_module_t的封裝可看做是Adapter模式的應用。

 

回到主線,前面的流程進行到Layer調用signalLayerUpdate()通知SurfaceFlinger就結束了。下面分析下SurfaceFlinger的流程及對於應用程序圖形緩衝區的後續處理過程。爲了讓文章看起來更加完整些,咱們仍是從SurfaceFlinger的初始化開始,而後再接着講那signalLayerUpdate()以後的故事。這裏就從SurfaceFlinger的建立開始講起吧。原本SurfaceFlinger有兩種啓動方式,由SystemServer以線程方式啓動,或是由init以進程方式啓動。不過Android 4.4上好像前者去掉了,反正我是沒找到。。。。那故事就從main_surfaceflinger.cpp講起吧。

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095104179.jpg

上半部分是SurfaceFlinger的初始化,下半部分是SurfaceFlinger對於VSync信號的處理,也就是一次合併渲染的過程。因爲代碼裏分支較多,爲了說明簡便,這裏做了幾點假設:首先假設平臺支持硬件VSync信號,這樣就不會建立VSyncThread來用軟件模擬了。另外假設INVALIDATE_ON_VSYNC爲1,也就是把INVALIDATE操做放到VSync信號來時再作。值得注意的是SurfaceFlinger線程模型相較以前版本有了較大變化,主要緣由是引入了VSync的虛擬化。相關的線程有EventThread(vsyncSrc), EventThread(sfVsyncSrc), EventControlThread和DispSyncThread。這個東西比較好玩,因此單獨放一篇文章來說(http://blog.csdn.net/jinzhuojun/article/details/17293325)。

 

先粗略介紹下幾個重要類的基本做用:

EventControlThread是一個簡單地另人髮指的線程,用來根據標誌位開關硬件VSync信號。但爲毛要單獨放到個線程,難道是開關硬件VSync信號的代價很大?

RenderEngine是對一坨egl和gl函數的封裝。引入它多是以爲egl和gl函數混雜在其它模塊中裏看起來太亂了。它把GL相關的東西封裝起來了,只暴露出GL無關的接口,這樣SurfaceFlinger等模塊做爲客戶端不須要了解底層細節。

Looper是一個通用的類,負責將事件和消息存成隊列,並提供輪詢接口處理隊列中的事件和消息。在這裏主要用於處理TRANSACTION, INVALIDATE和REFRESH消息以及VSync事件。

MessageQueue主要操做Looper。因爲Looper不是專用於SurfaceFlinger的,MessageQueue封裝了一些SurfaceFlinger專用的信息,使得EventThread和SurfaceFlinger等模塊能夠經過它來間接使用Looper類。

SurfaceFlinger,DisplayDevice,FramebufferSurface和HWComposer這幾個類之間的關係比較曖昧。SurfaceFlinger是這部分裏最大的客戶端。DisplayDevice抽象了顯示設備,封裝了用於渲染的Surface和HWComposer模塊等,從而儘量使得SurfaceFlinger只要和它打交道。FramebufferSurface抽象了SurfaceFlinger用來畫圖的Surface,該Surface對應一個BufferQueue用於多緩衝渲染。它和以前提到的應用端用到的Surface區別在於它是基於硬件圖形緩衝區的,而不是Ashmem。HWComposer封裝了兩個重要的HAL模塊,即framebuffer和hwcomposer,前者對應硬件緩衝區,後者對應hwcomposer硬件。hwcomposer控制VSync信號,管理屏幕信息和操做framebuffer。HWComposer主要工做是負責將平臺相關的HAL模塊加載好,而且使得上層模塊經過它來使用HAL模塊。注意兩個容易混淆的概念:HWComposer是類,確定會有;hwcomposer是硬件模塊,在某些平臺可能會不可用。這幾個類的大體結構以下:

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095104180.jpg

SurfaceFlinger的渲染工做主要是由VSync信號驅動的。EventThread負責發出虛擬VSync信號(由硬件VSync信號偏移指定相位虛擬化獲得)。初始化時,MessageQueue在setEventThread()函數中先與EventThread創建鏈接,而後將與EventThread之間通訊的socket(BitTube)句柄註冊進Looper,同時也註冊了本身的回調函數。另外一方面,SurfaceFlinger會經過Looper不斷輪詢這個句柄,看該句柄上有沒有數據。當在該句柄上接收到數據,就會調用MessageQueue相應的回調函數。通過一番處理後,最後MessageQueue的Handler基於收到的消息調用到SurfaceFlinger的相應處理函數。就這樣,EventThread->Looper->MessageQueue->SurfaceFlinger的事件消息傳遞過程造成了。

 

這裏又看到了比較熟悉的設計模式,如Iterator模式用於遍歷全部圖層(HWComposer::LayerListIterator), Observer模式用於虛擬VSync信號線程向DispSyncThread申請虛擬VSync事件(DispSyncThread::EventListener),還有前面提到過的Command模式用於消息的延遲處理(postMessageAsync())。

 

除了這些熟悉的身影外,咱們還能看到些新面孔。如RenderEngine::create()應用了簡單工廠模式,它根據GLES版本號建立相應的RenderEngine。這樣,做爲RenderEngine的使用者SurfaceFlinger和Layer天然就成了Strategy模式的受益者,它們不用關心RenderEngine在各個不一樣版本間實現的差別。還有Mediator模式在Service管理中的應用。SurfaceFlinger進程中,addService()函數向Service Manager註冊了SurfaceFlinger服務。這樣Service Manager做爲中介的角色在Client和Service之間作溝通,它使得一個網狀的模塊結構變成了一個優美的星形結構。固然在這裏由於就一個服務看不出來,所以這個另做章節再講。

 

能夠看到,當VSync信號到來時,SurfaceFlinger最主要是經過處理INVALIDATE和REFRESH消息來作合併渲染和輸出的工做的。這裏的核心思想是能少處理一點是一點,因此在渲染前有不少髒區域的計算工做,這樣後面只要處理那些區域的更新就能夠了。這樣是有現實意義的,一方面因爲圖層間的遮蓋,有些不可見圖層不須要渲染。另外一方面,由於咱們的應用程序中先後幀通常只有一小部分變化,要是每幀都全變估計人都要看吐了。這裏主要是調用了這幾個函數:

handleMessageTransaction()主要處理以前對屏幕和應用程序窗口的改動。因這些改動頗有可能會改變圖層的可見區域,進而影響髒區域的計算。

handleMessageInvalidate()主要調用handlePageFlip()函數。這裏Page Flip是指從BufferQueue中取下一個圖形緩衝區內容,就好像是「翻頁」同樣。該函數主要是從各Layer對應的BufferQueue中拿圖形緩衝區數據,並根據內容更新髒區域。

handleMessageRefresh()就是合併和渲染輸出了。做爲重頭戲,這步步驟多一些,大致框架以下:

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20131223/20131223095105182.jpg

文章一開始的測試用例主幹部分背後的故事大概就這麼些了。篇幅有限,省略了不少細節。咱們能夠看到,在服務端作了這麼多的事,而對於應用程序來講只要先lock(),再填buffer,最後unlockAndPost()就OK了。這也算是Facade模式的體現了吧。

 

總結地說,從Android源碼中咱們能夠溫習到應用設計模式的基本原則:一是隻在合適的地方用。不少時候咱們學習設計模式偏偏是爲了避免用,準確地說是不濫用。二是要用的話不須要過於拘泥於原有的或經典的用法,以解決問題爲目的適當自由發揮。

相關文章
相關標籤/搜索