1. 程式人生 > >Android應用程式視窗(Activity)實現框架簡要介紹和學習計劃

Android應用程式視窗(Activity)實現框架簡要介紹和學習計劃

        前面我們學習了SurfaceFlinger服務的實現原理。有了這個基礎之後,從本文開始,我們就可以分析Android系統在Java層的UI實現了。我們知道,在Android應用程式的四大元件中,只有Activity元件與UI相關,它描述的是應用程式視窗,因此,我們就通過它的UI實現來分析Android系統在Java層的UI實現。本文主要是對Activity元件的UI實現作簡要介紹以及制定學習計劃。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!

        Activity元件的UI實現需要與WindowManagerService服務和SurfaceFlinger服務進行互動。從前面

Android應用程式鍵盤(Keyboard)訊息處理機制分析一文可以知道,Activity元件在啟動完成後,會通過一個型別為Session的Binder物件來請求WindowManagerService為它建立一個型別為WindowState的物件,用來描述它的視窗狀態。此外,從Android應用程式與SurfaceFlinger服務的關係概述和學習計劃這一系列的文章又可以知道,Android應用程式會通過一個型別為Client的Binder物件來請求SurfaceFlinger服務為它建立一個型別為Layer的物件,用來描述它的視窗資料。

        從Android應用程式請求SurfaceFlinger服務建立Surface的過程分析

一文又可以知道,SurfaceFlinger服務為Android應用程式建立一個型別為Layer的物件之後,會返回一個型別為SurfaceLayer的Binder物件給Android應用程式,這樣Android應用程式就可以通過這個Binder物件來請求SurfaceFlinger服務來分配圖形緩衝區。

        綜合上述資訊,我們就可以得到Activity元件與WindowManagerService服務和SurfaceFlinger服務的互動模型,如圖1所示:


圖1 Activity元件與WindowManagerService服務和SurfaceFlinger服務的互動模型

        事實上,用來關聯Activity元件和Layer物件的SurfaceLayer物件並不是由Android應用程式請求SurfaceFlinger服務來建立的,而是由WindowManagerService服務請求SurfaceFlinger服務來建立的。WindowManagerService服務得到這個SurfaceLayer物件之後,再將它的一個代理物件返回給在Android應用程式這一側的Activity元件。這樣,Activity元件和WindowManagerService服務就可以通過同一個SurfaceLayer物件來操作在SurfaceFlinger服務這一側的Layer物件,而操作Layer物件的目的就是為了修改Activity元件的UI。

        在前面Android應用程式與SurfaceFlinger服務的關係概述和學習計劃Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃這兩個系列的文章中,我們已經分析在SurfaceFlinger服務這一側的Layer類和SurfaceLayer類的實現了。在現在的這一系列文章中,我們主要分析在Android應用程式這一側的Activity元件與UI相關的類的實現,以及在WindowManagerService服務這一側的WindowState類的實現。

        我們首先看Activity元件的實現,如圖2所示:


圖2 Activity元件的類關係圖

        Activity類是從ContextThemeWrapper類繼承下來的,而ContextThemeWrapper類又是從ContextWrapper類繼承下來的,最後ContextWrapper類又繼承了Context類。

        從前面Android應用程式啟動過程原始碼分析一文可以知道,Activity元件在啟動的過程中,系統會為它建立一個ContextImpl物件,用來描述它的執行上下文環境。這個ContextImpl物件首先是通過呼叫Acitivity類的成員函式attach傳遞到Activity元件內部,接著再依次通過呼叫父類ContextThemeWrapper和ContextWrapper的成員函式attachBaseContext來分別儲存在它們的成員變數mBase中。因此,ContextThemeWrapper和ContextWrapper類的成員變數mBase指向的實際上是一個ContextImpl物件。

       從前面Android應用程式啟動過程原始碼分析一文還可以知道,系統為一個正在啟動的Activity元件建立了一個ContextImpl物件之後,還會呼叫這個ContextImpl物件的成員函式setOuterContext來將正在啟動的Activity元件儲存在其成員變數mOuterContext中。這樣,一個Activity元件就可以通過其父類ContextThemeWrapper或者ContextWrapper的成員變數mBase來訪問用來描述它的執行上下文環境的一個ContextImpl物件,同時,一個ContextImpl物件也可以通過它的成員變數mOuterContext來訪問它的宿主Activity元件。

      Activity類還有另外一個型別為WindowManager的成員變數mWindowManager,它實際上指向的一個LocalWindowManager物件。LocalWindowManager類是用來管理應用程式視窗的,例如,用來維護應用程式視窗內部的檢視(View)。LocalWindowManager類有一個型別為WindowManager的成員變數mWindowManager,它實際上指向的是一個WindowManagerImpl物件。系統通過呼叫WindowManagerImpl類的靜態成員函式getDefault來獲得一個WindowManagerImpl物件,然後儲存在LocalWindowManager類的成員變數mWindowManager中。這樣,LocalWindowManager類就可以通過WindowManagerImpl類來真正實現管理應用程式視窗的功能。

      從上面的分析中,我們還看不出的一個Activity元件的視窗是如何描述的。為了弄清楚這個問題,我們繼續分析Activity類的另外一個成員變數mWindow,如圖3所示:


圖3 Window類的實現

        Activity類的成員變數mWindow的型別為Window,它用來描述一個應用程式視窗。這樣,通過這個成員變數,每一個Activity元件就都會有一個對應的Window物件,即一個對應的應用程式視窗。

        Window類有一個型別為Context的成員變數mContext。這個成員變數指向的是一個Activity物件。當系統為一個Activity元件建立一個對應的Window物件時,就會將這個Activity元件的Context介面儲存在這個對應的Window物件的成員變數mContext中。這樣,一個Window物件就可以通過它的成員變數mContext來訪問它所描述的Activity元件的資源。

        Window類還有一個型別為Window.Callback的成員變數mCallback。這個成員變數和成員變數mContext一樣,都是指向同一個Activity物件,因為Activity類是實現了Window.Callback介面的。當系統為一個Activity元件建立一個對應的Window物件時,就會將這個Activity元件所實現的Window.Callback介面通過Window類的成員函式setCallback儲存在對應的Window物件的成員變數mCallback。這樣,一個Window物件就可以通過它的成員變數mCallback來將一些事件交給與它所對應的Activity元件來處理,例如,將接收的鍵盤事件交給對應的Activity元件來處理。

        最後,Window類還有一個型別為WindowManager的成員變數mWindowManager。這個成員變數指向的是一個LocalWindowManager物件。前面提到,Activity元件的成員變數mWindowManager指向的也是一個LocalWindowManager物件。系統在啟動一個Activity元件的過程中,會通過Window類的成員函式setWindowManager來將儲存在它的成員變數mWindowManager中的一個LocalWindowManager物件也儲存在對應的Window物件的成員變數mWindowManager。這樣,一個Activity元件以及它所對應的Window物件就可以使用同一個LocalWindowManager物件來管理它們所描述的UI了。

       事實上,Activity類的成員變數mWindow指向的並不是一個Window物件,而是一個PhoneWindow物件。也就是說,一個Activity元件的UI是使用一個PhoneWindow物件來描述的。

       Activity類的成員變數mWindow所指向的一個PhoneWindow物件是通過呼叫PolicyManager類的靜態成員函式makeNewWindow來建立的。PolicyManager類的實現如圖4所示:


圖4 PolicyManager類的實現

        PolicyManager類有一個型別為IPolicy的靜態成員變數sPolicy,它實際指向的是一個Policy物件。Policy類實現了IPolicy介面的成員函式makeNewWindow,而PolicyManager類就是通過這個成員函式來為一個Activity元件建立一個PhoneWindow物件的。

       PhoneWindow類繼承了Window類,因此,它的物件可以儲存Activity類的成員變數mWindow中。PhoneWindow類的實現如圖5所示:


圖5 PhoneWindow類的實現

        PhoneWindow類有兩個重要的成員變數mDecor和mContentParent,它們的型別分別DecorView和ViewGroup。其中,成員變數mDecor是用描述自己的視窗檢視,而成員變數mContentParent用來描述檢視內容的父視窗。

        DecorView類繼承了FrameLayout類,而FrameLayout類又繼承了ViewGroup類,最後ViewGroup類又繼承了View類。View類有一個成員函式draw,它是用來繪製應用程式視窗的UI的。DecorView類、FrameLayout類和ViewGroup類都重寫了父類的成員函式draw,這樣,它們就都可以定製自己的UI。

        DecorView類所描述的應用程式視窗檢視是否需要重新繪製是由另外一個類ViewRoot來控制的。系統在啟動一個Activity元件的過程中,會為這個Activity元件建立一個ViewRoot物件,同時還會將前面為這個Activity元件所建立的一個PhoneWindow物件的成員變數mDecor所描述的一個檢視(DecorView)儲存在這個ViewRoot物件的成員變數mView中。這樣,這個ViewRoot物件就可以通過呼叫它的成員變數mView的所描述的一個DecorView的成員函式draw來繪製一個Acitivity元件的UI了。ViewRoot類的作用是非常大的,它除了用來控制一個Acitivity元件的UI繪製之外,還負責接收Acitivity元件的IO輸入事件,例如,鍵盤事件,這一點可以參考前面Android應用程式鍵盤(Keyboard)訊息處理機制分析一文。

        ViewRoot類的實現如圖6所示:


圖6 ViewRoot類的實現

        ViewRoot類是從Handler類繼承下來的。從前面Android應用程式訊息處理機制(Looper、Handler)分析一文可以知道,從Handler類繼承下來的子類可以呼叫父類的成員函式sendMessage來向指定的執行緒的訊息佇列傳送訊息,以及在自己重寫的成員函式handleMessage中處理該訊息。 ViewRoot類在兩種情況需要經常應用程式程序的主執行緒的訊息佇列傳送訊息。

        第一種情況是當ViewRoot類從系統輸入管理器InputManager接收到鍵盤、觸控式螢幕等輸入事件時,它就會把這些輸入事件封裝成一個訊息,並且傳送到應用程式程序的主執行緒的訊息佇列中去進一步處理,這樣就可以保證鍵盤、觸控式螢幕等輸入事件可以在應用程式程序的主執行緒中進行處理。這一點可以參考前面Android應用程式鍵盤(Keyboard)訊息處理機制分析一文。

        第二種情況是當ViewRoot類需要重新繪製與它所關聯的一個Activity元件的UI時,它就會將這個繪製UI的操作封裝成一個訊息,並且傳送到應用程式程序的主執行緒的訊息佇列中去進一步處理,這樣同樣可以保證繪製UI的操作可以在應用程式程序的主執行緒中執行。

        每一個ViewRoot物件都有一個型別為View的成員變數mView,它指向了一個DecorView物件。這個DecorView物件是從哪裡來的呢?前面提到,每一個Activity元件都有一個對應的ViewRoot物件以及一個對應的PhoneWindow物件,這個DecorView物件就是來自於這個對應的PhoneWindow物件的成員變數mDecor。也就是說,與同一個Activity元件對應的ViewRoot物件和PhoneWindow物件分別通過各自的成員變數mView和mDecor引用了共一個DecorView物件。

        每一個ViewRoot物件都有一個型別為WindowManager.LayoutParams的成員變數mWindowAttributes,它指向了一個ViewGroup.LayoutParams物件,用來描述與該ViewRoot物件對應的一個Activity元件的UI佈局資訊。

        從上面的描述就可以知道,每一個Activity元件都有一個對應的ViewRoot物件、View物件以及WindowManager.LayoutParams物件。這三個物件的對應關係是由WindowManagerImpl類來維護的。具體來說,就是由WindowManagerImpl類的成員變數mRoots、mViews和mParams所描述的三個陣列來維護的。例如,假設一個應用程式程序執行有兩個Activity元件,那麼WindowManagerImpl類的成員變數mRoots、mViews和mParams所描述的三個陣列的大小就等於2,其中,mRoots[0]、mViews[0]和mParams[0]對應於第一個啟動的Activity元件,而mRoots[1]、mViews[1]和mParams[1]對應於第二個啟動的Activity元件。

        每一個ViewRoot物件都有一個型別為Surface的成員變數mSurface,它指向了一個Java層的Surface物件。這個Java層的Surface物件通過它的成員變數mNativeSurface與一個C++層的Surface物件。這個C++層的Surface物件就是我們在Android應用程式與SurfaceFlinger服務的關係概述和學習計劃這一系列文章中所分析的Surface類的例項了。這個Surface類是用來在Android應用程式程序這一側描述應用程式視窗的。從前面Android應用程式請求SurfaceFlinger服務建立Surface的過程分析一文可以知道,在C++層中,每一個Surface物件都有一個對應的SurfaceControl物件。這個對應的SurfaceControl物件是用來設定應用程式視窗的屬性,例如,設定大小、位置等屬性。

        但是,與ViewRoot類的成員變數mSurface所對應的在C++層的Surface物件並沒有一個對應的SurfaceControl物件,這是因為ViewRoot類並不需要設定應用程式視窗的屬性,它需要做的只是往應用程式視窗的圖形緩衝區填充UI資料,即它需要設定的只是應用程式視窗的紋理。應用程式視窗的紋理儲存在Java層的Surface類的成員變數mCanvas所描述一個畫布(Canvas)中,即通過這個畫布可以訪問到應用程式視窗的圖形緩衝區。當ViewRoot類需要重新繪製與它對應的Activity元件的UI時,它就會呼叫它的成員函式draw來執行這個繪製的操作。ViewRoot類的成員函式draw首先通過獲得儲存它的成員變數mSurface內部的一塊畫布,然後再將這個畫布傳遞給它的成員變數mView所描述的一個View物件的成員函式draw。View類的成員函式draw得到了這塊畫布之後,就可以隨心所欲地上面繪製應用程式視窗的紋理了。這些紋理的繪製工作是通過Skia圖形庫API來進行的。

        那麼,應用程式視窗的屬性是由誰來管理的呢?這是由WindowManagerService服務來管理的。前面提到,在Android應用程式這一側的Activity元件是由WindowManagerService服務來為它請求SurfaceFlinger服務建立一個Layer物件以及一個SurfaceLayer物件的。這個SurfaceLayer物件建立完成之後,WindowManagerService服務就會將它封裝在一個Java層的Surface物件中,以後就可以通過這個Java層的Surface物件來請求SurfaceFlinger服務設定一個對應的應用程式視窗的屬性。

        由於Java層的Surface物件實現了Parcelable介面,因此,WindowManagerService服務在為一個Activity元件請求SurfaceFlinger服務建立一個Layer物件以及一個SurfaceLayer物件之後,就可以將得到的Java層的Surface物件跨程序地返回給該Activity元件。Activity元件得到這個Surface物件之後,再使用儲存在裡面的SurfaceLayer物件來初始化與它所對應的一個ViewRoot物件的成員變數mSurface所描述的一個Java層的Surface物件。

        那麼,WindowManagerService服務又是什麼時候會為一個Activity元件請求SurfaceFlinger服務建立一個Layer物件以及一個SurfaceLayer物件呢?ViewRoot類有一個型別為IWindowSession的靜態成員變數sWindowSession,它指向的實際上是一個實現了IWindowSession介面的Binder物件。這個Binder物件的型別為Session,執行在WindowManagerService服務這一側。當一個Activity元件的UI第一次要被繪製之前,它所執行在的應用程式程序就會通過ViewRoot類的靜態成員變數sWindowSession來向WindowManagerService服務傳送一個請求。WindowManagerService服務接收到這個請求之後,再請求SurfaceFlinger服務為這個Activity元件建立一個Layer物件以及一個SurfaceLayer物件。這樣,這個Activity元件的UI才能真正地繪製在螢幕中。

        至此,我們就簡要分析完成了在Android應用程式這一側的Activity元件與UI相關的類的實現,接下來我們繼續分析在WindowManagerService服務這一側的WindowState類的實現。

        WindowState類的實現如圖7所示:


圖7 WindowState類的實現

        在Android應用程式這一側,每一個Activity元件在WindowManagerService服務這一側都有一個對應的WindowState物件,用來描述Activity元件的視窗狀態。WindowState類有兩個重要的成員變數mSession和mSurface,它們的型別分別為SurfaceSession和Surface。接下來,我們就描述這兩個成員變數的作用。

        前面提到,Activity元件是在啟動完成之後,請求WindowManagerService服務為它建立一個WindowState物件的。建立完成這個WindowState物件之後,WindowManagerService服務再呼叫它的成員函式attach來為它附加一個SurfaceSession物件。WindowState類的成員函式attach又是通過呼叫它的成員變數mSession所描述的一個Session物件的成員函式windowAddedLocked來附加一個SurfaceSession物件的。

        Session類有一個型別為SurfaceSession的成員變數mSurfaceSession。當WindowState類的成員函式attach呼叫Session類的成員函式windowAddedLocked來為一個WindowState物件附加一個SurfaceSession物件的時候,後者首先會檢查它的成員變數mSurfaceSession是否已經指向了一個SurfaceSession物件。如果如果指向了的話,那麼Session類的成員函式windowAddedLocked就什麼也不用做,否則的話,Session類的成員函式windowAddedLocked就會建立一個SurfaceSession物件,並且儲存在它的成員變數mSurfaceSession中。

        SurfaceSession類有一個型別為int的成員變數mClient,它儲存的是一個C++層的SurfaceComposerClient物件的地址,即每一個Java層的SurfaceSession物件在C++層都有一個對應的SurfaceComposerClient物件。當一個SurfaceSession物件建立的時候,與它所關聯的SurfaceComposerClient物件也會同時被建立。從前面Android應用程式與SurfaceFlinger服務的連線過程分析一文可以知道,SurfaceComposerClient類用來描述Android應用程式程序與SurfaceFlinger服務之間的一個連線,即每一個與UI相關的Android應用程式程序都有一個SurfaceComposerClient物件。

        讀者可能會覺得奇怪,既然SurfaceComposerClient是用來描述Android應用程式程序與SurfaceFlinger服務的連線的,那麼為什麼WindowManagerService服務會在內部建立SurfaceComposerClient物件呢?由於WindowManagerService需要請求SurfaceFlinger服務來設定Android應用程式視窗的屬性,例如,設定應用程式視窗的位置、大小等,因此,它就需要為每一個Android應用程式程序建立一個SurfaceComposerClient物件連線到SurfaceFlinger服務中去,以便可以和SurfaceFlinger服務進行通訊。

        從上面的描述我們就可以知道,在WindowManagerService服務中,每一個Android應用程式程序都對應有一個SurfaceComposerClient物件。由於每一個SurfaceComposerClient物件都關聯有一個SurfaceSession物件,因此,我們又可以推斷出每一個Android應用程式程序在WindowManagerService服務中都對應有一個SurfaceSession物件。由於每一個SurfaceSession物件所屬的Session物件是一個Binder本地物件,並且它的Binder代理物件是儲存在Android應用程式程序這一側的ViewRoot類的靜態成員變數sWindowSession中,因此,我們又可以推斷出每一個Android應用程式程序在WindowManagerService服務都有一個對應的Session物件。綜合起來就是,每一個Android應用程式程序在WindowManagerService服務這一側對應有一個Session物件、一個SurfaceSession物件以及一個SurfaceComposerClient物件。由於每一個Android應用程式程序都可以執行若干個Activity元件,因此,我們又可以說,Activity元件與WindowServiceManager服務這一側的Session物件、SurfaceSession物件以及SurfaceComposerClient物件是多對一的關係。

        介紹了WindowState類的成員變數mSession之後,我們接著介紹另外一個成員變數mSurface,它的型別為Surface,前面我們已經介紹過Surface類在Android應用程式程序這一側的作用了,接下來我們就介紹它在WindowManagerService這一側的作用。

       前面提到,WindowManagerService服務會在內部為每一個應用程式視窗,即每一個Activity元件,建立一個SurfaceLayer物件,這個SurfaceLayer物件是封裝成一個Java層的Surface物件中的。在Java層的Surface類中,有一個型別為int的成員變數mSurfaceControl,它儲存的是在C++層的一個SurfaceControl物件的地址值,即在WindowManagerService服務這一側,每一個Java層的Surface物件在 C++層都有一個對應的SurfaceControl物件。這裡我們強調是在WindowManagerService服務這一側,是因為在前面提到,在Android應用程式這一側,每一個Activity元件所對應的Java層的Surface物件在C++層是沒有一個對應的SurfaceControl物件,而只是對應有一個C++層的Surface物件。通過C++層的SurfaceControl物件可以設定應用程式視窗的屬性,而通過C++層的Surface物件則可以設定應用程式視窗的圖形緩衝區,即設定應用程式視窗的紋理,因此,我們就可以知道應用程式視窗的屬性是由WindowManagerService服務來設定的,而應用程式視窗的紋理是由它所在的程序負責設定的。

       至此,我們就簡要地介紹了Android應用程式視窗的實現框架了。上面所介紹的類及其互動關係可能會比較模糊,不易理解。不要緊,接下來我們會通過一系列的文章來弄清楚它們的來龍去脈:

       學習了這些文章,我們就可以掌握Android應用程式視窗的實現框架了。掌握了Android應用程式視窗的實現框架之後,我們就可以再進一步去詳細地學習Android應用程式視窗的渲染過程。敬請關注!