Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 4.4(KitKat)窗口管理子系統 - 體系框架

Android 4.4(KitKat)窗口管理子系統 - 體系框架

編輯:關於Android編程

窗口管理系統是Android中的主要子系統之一,它涉及到App中組件的管理,系統和應用窗口的管理和繪制等工作。由於其涉及模塊眾多,且與用戶體驗密切相關,所以它也是Android當中最為復雜的子系統之一。一個App從啟動到主窗口顯示出來,需要App,ActivityManagerService(AMS),WindowManagerService(WMS),SurfaceFlinger(SF)等幾個模塊相互合作。App負責業務邏輯,繪制自己的視圖;AMS管理組件、進程信息和Activity的堆棧及狀態等等;WMS管理Activity對應的窗口及子窗口,還有系統窗口等;SF用於管理圖形緩沖區,將App繪制的東西合成渲染在屏幕上。下面分幾個部分進行分析。

窗口管理系統的主要框架及各模塊之間的通訊接口大體如下:

\

<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+u/nT2kJpbmRlcrXEsb612Ln9s8y199PDo6hMUEOjqcjDQW5kcm9pZLXExKO/6bzk8e66z7bIuPy1zaOsveG5ubj8vNPH5c76oaPDv7j2xKO/6bj3y77G5Nawo6yyos/yxuTL/MSjv+nM4bmpvdO/2qGjvfizzLrNdWlk1eLQqUxpbnV41tC1xLv61sa21NXi0KnEo7/pzOG5qcHLzOzIu7XEsaO7pKOsyrm1w8+1zbO4/LzTwrOw9KGjxKO/6dauvOS+rbOjyrnTw0MvU7XEveG5uaOstvhTZXJ2aWNlsb7J7dKyv8nE3MrHyrnTw8bky/xTZXJ2aWNltcRDbGllbnSho77ZwP3AtMu1o6zI57n7U2VydmljZbXEyrXP1r3QWFhYTWFuYWdlclNlcnZpY2WjrMTH0ruw48v8ttRDbGllbnTM4bmpvdO/2klYWFhNYW5hZ2Vyo6zIu7rzQ2xpZW500qrTw1NlcnZpY2W1xMqxuvKx47vhyerH69K7uPa90EJwWFhYTWFuYWdlcrXEtPrA7bbUz/OjrMv8ysfUtrbLQm5YWFhNYW5hZ2Vysb612LbUz/PU2kNsaWVudLbLtcS0+sDtoaO0+sDtttTP80JwWFhNYW5hZ2VyyrXP1sHLSVhYWE1hbmFnZXK1xMv509C907/ao6zWu7K7uf3A78PmtcS6r8r9trzKx7/H19OjrNa7uLrU8LLOyv21xNe8sbijrMi7uvO+zbX308PUtrbLttTP88il1rTQ0KGj1La2y7XEtcRCblhYWE1hbmFnZXK21M/zvLDG5LzMs9DA4MrH1ebV/df2ysK1xKOsQm5YWFhNYW5hZ2VyvMyz0NfUSVhYWE1hbmFnZXIuU3R1YrPpz/PA4KOsyrXP1sHLSVhYWE1hbmFnZXK907/aoaNTdHVivs3I58bkw/vX1tK70fmjrMrHQm5YWFhNYW5hZ2VytcS8zLPQwODU2kJuWFhYTWFuYWdlctbQtcShsLmz19OhsaGjzai5/bX308PV4tCpvdO/2rHjv8nS1LX308O1vdS2tsu1xFNlcnZpY2W5psTcwcuho7jFxO7Jz8DgJiMyMDI4NDvUtrPMZ2RitffK1KOsaG9zdLv6yc+1xGdkYrrNZ3Vlc3TJz7XEZ2Ric2VydmVyz+DBrNLUuvOjrNTaaG9zdMnPx8PD/MHuu+HIw2dkYnNlcnZlcsil1rTQ0KOstau40L71vs3P8crH1Npob3N0sb612Na00NDSu9H5oaPV4rb5tcRnZGJzZXJ2ZXK+zczhuanBy8DgJiMyMDI4NDvT2lN0dWK1xLmmxNyhozwvcD4KPHA+IDwvcD4KPHA+1eLW1tS2s8y199PDxKPQzbXEvajBorn9s8zSu7Djyse31rLjtM61xKGjscjI51dpbmRvd01hbmFnZXJHbG9iYWy74dPrV01TvfjQ0MGsvdOjrFZpZXdSb290SW1wbLvh0+tXTVPW0LXEU2Vzc2lvbr340NDBrL3To6y437Ljz8jT67jfsuPNqNDFo6zNrMqxsO/W+r2owaK1zbLjvOS1xM2o0MWjrMi7uvO1zbLj0+u1zbLj1rG9082o0MWho7TyuPaxyLe9o6zVxcj9ysdBsr/DxbXE1LG5pKOsy/vP69Kqus1Csr/DxbrP1/e449K7uPa77ravo6zL+9K7sOOyu7vh1rG907Pluf3IpUKyv8PFsKS49s7KtcSho8v50tTL+8/Ius3X1Ly6tcTW97ncwO7LxMu1o6zO0tKqus1Csr/DxbrP1/ejrNPaysfA7svE1dK1vUKyv8PFtcTW97nczfXO5aOsy7XE47P2uPbIy7DJoaPT2srHzfXO5brN1dTB+cu1o6zE47i61PDV4srCtvmwyaOssqK45svfwctBsr/Dxdb3udzA7svEoaPA7svE1Nm45svfz8LK9NXFyP2jrNXUwfnKx0Kyv8PFvdO/2sjLo6zE49LUuvPWsb3Tus3L+8Gqz7WwyaGj09rKx9XFyP26zdXUwfnS1Lrzvs3Wsb3TwarPtcHLoaPI57n7us/X99bQ09DQ6NKqs6zUvdfUvLrIqM/etcSy2df3o6zL+8PH1NnP8rj319S1xNb3udzJ6sfroaOxyMjnQXBw0+tXTVO1xMGsvdOjrMrXz8i74b2owaLSu7j2U2Vzc2lvbrW9V01To6zWrrrzvs274c2ouf1JV2luZG93U2Vzc2lvbr3Tv9rT61dNU9bQtcRTZXNzaW9u1rG9082o0MWho7u509DA/cjnV01Tus1TRs/ItLS9qFN1cmZhY2VTZXNzaW9uo6zG5NbQu+G0tL2oU3VyZmFjZUNvbXBvc2VyQ2xpZW50o6y3w87KU3VyZmFjZUNvbXBvc2VyQ2xpZW50yrG74dTaU0bW0LS0vahDbGllbnTT69auttTTpqOs1eK49kNsaWVudMq1z9bBy0lTdXJmYWNlQ29tcG9zZXJDbGllbnS907/ao6zWrrrzU3VyZmFjZUNvbXBvc2VyQ2xpZW50u+HNqLn9uMO907/a0+tTRtbQtcRDbGllbnTWsb3TzajQxaGjPC9wPgo8cD4gPC9wPgo8cD6/tLT6wuu5/bPM1tCjrLj3uPa21M/zvOS1xMr9wb+8sLbU06a52M+1vq2zo8jDyMu77M/9o6zPwsPmwdC+2cHL1NrSu7Djx+m/9s/CuPe21M/z1q685LXEyrXM5bnYz7XNvKGjxuTW0LHqyau1xMrHz+DTptfTz7XNs9bQsci9z7v5tKG6y9DEtcTA4KGjPC9wPgo8cD48aW1nIHNyYz0="/uploadfile/Collfiles/20140715/20140715085737122.jpg" alt="\">

要注意的幾點:1. App中可以沒有Activity,也可以沒有PhoneWindow和DecorView,比如一個顯示浮動窗口的Service。2. Task中的Activity可以來自不同進程,比如App運行過程中打開相機App拍照。3. WindowState代表WMS中的一個窗口,這和App端的Window類是不一樣的,盡管很多時候一個Window類(即PhoneWindow)有一個對應的WindowState,但那不是絕對的。一個Activity在WMS中有對應的AppWindowToken,一個AppWindowToken又可以包含多個WindowState,因為除了主窗口外,還可能有子窗口和啟動窗口。此外對於系統窗口,WindowState還可能不對應AppWindowToken。4.這裡的Application指的是App端的一個進程,它不同於AndroidManifest.xml中的標簽。後者是配置文件中對組件的管理者,它和進程之間沒有本質關系,通過android:process標簽可以讓同一個下的組件跑在多個進程,也可以讓多個中的組件跑在同一個進程。所以如果是定義的Application的話和ProcessRecord就是m:n的關系了。以下談到Application都是指一個App的進程。


首先分析下App端的結構。移動平台一般顯示區域有限,要完成一個工作往往不是一屏內容中能搞定的,所以Android中有了Activity的概念,讓用戶可以把相關的子內容放到單獨的Activity中,然後通過Intent在Activity間跳轉。類似於浏覽網頁,點擊鏈接跳轉到另一個網頁。這些同一交互過程中的一系列Activity成為一個Task。這些Activity運行在主線程ActivityThread中。Activity要展現出來的主視圖是DecorView,它是一棵視圖樹。ViewRootImpl負責管理這個視圖樹和與WMS交互,與WMS交互通過WindowManagerImpl和WindowManagerGlobal。DecorView被包含在系統的通用窗口抽象類Window當中。視圖對應的圖形緩沖區由Surface管理。其中涉及到的主要的類包括下面幾個:

Activity:描述一個Activity,它是與用戶交互的基本單元。

ActivityThread:每一個App進程有一個主線程,它由ActivityThread描述。它負責這個App進程中各個Activity的調度和執行,以及響應AMS的操作請求等。

ApplicationThread:AMS和Activity通過它進行通信。對於AMS而言,ApplicationThread代表了App的主線程。簡而言之,它是AMS與ActivityThread進行交互的接口。注意ActivityThread和ApplicationThread之間的關系並不像Activity與Application。後者的關系是Application中包含了多個Activity,而前者ActivityThread和ApplicationThread是同一個東西的兩種"View",ApplicationThread是在AMS眼中的ActivityThread。

ViewRootImpl:主要責任包括創建Surface,和WMS的交互和App端的UI布局和渲染。同時負責把一些事件發往Activity以便Activity可以截獲事件。每一個添加到WMS中的窗口對應一個ViewRootImpl,通過WindowManagerGlobal向WMS添加窗口時創建。大多數情況下,它管理Activity頂層視圖DecorView。總得來說,它相當於MVC模型中的Controller。

ViewRootImpl::W:用於向WMS提供接口,讓WMS控制App端的窗口。它可看作是個代理,很多時候會調用ViewRootImpl中的功能。這種內嵌類的用法很多,特別是這種提供接口的代理類,如PhoneWindow::DecorView等。

Instrumentation:官方提供的Hook,主要用於測試。如果只關注窗口管理流程的話可以先無視。

WindowManagerImpl:Activity中與窗口管理系統通信的代理類,實現類是WindowManagerGlobal。WindowManagerGlobal是App中全局的窗口管理模塊,因此是個Singleton。其中管理著該App中的ViewRootImpl,DecorView等結構,以有兩個Activity的App為例:

\

Window:每個App雖然都可以做得各不相同,但是作為有大量用戶交互的系統,窗口之間必須要有統一的交互模式,這樣才能減小用戶的學習成本。這些共性比如title, action bar的顯示和通用按鍵的處理等等。Window類就抽象了這些共性。另外,它定義了一組Callback,Activity通過實現這些Callback被調用來處理事件。注意要和在WMS中的窗口區分開來,WMS中的窗口更像是App端的View。

PhoneWindow:PhoneWindow是Window類的唯一實現,至少目前是。這樣的設計下如果要加其它平台的Window類型更加方便。每個Activity會有一個PhoneWindow,在attach到ActivityThread時創建,保存在mWindow成員中。

Context:運行上下文,Activity和Service本質上都是一個Context,Context包含了它們作為運行實體的共性,如啟動Activity,綁定Service,處理Broadcast和Receiver等等。注意Application也會有Context。Activity的Context是對應Activity的,Activity被殺掉(比如轉屏後)後就變了。所以要注意如果有生命周期很長的對象有對Activity的Context的引用的話,轉屏、返回這種會引起Activity銷毀的操作都會引起內存洩露。而Application的Context生命周期是和App進程一致的。關於Context的類結構圖有下面的形式。Context是抽象類,定義了接口。ContextImpl是Context的實現類,包含了實現。而ContextWrapper是Context的包裝類,它把請求delegate給其中的ContextImpl類去完成。ContextThemeWrapper是ContextWrapper的裝飾類,它在ContextWrapper的基礎上提供了自定義的主題。這結構初看有點亂,但結合下面的Decorator模式就一目了然了。

\

Surface:這是在App端管理圖形緩沖區的類,其中最重要的是圖形緩沖區隊列。經由WMS從SF中得到IGraphicBufferProducer接口對象BufferQueue後,Surface便可以從該隊列中queue和dequeue圖形緩沖區。SurfaceControl在WMS中封裝了Surface以及與SF的交互。Canvas和Surface從字面意思上很像,但前者其實更確切地說不是“畫布”,而是“畫家”。Surface中的圖形緩沖區才是App的畫布。

上面這些基本類之間的主要關系如下:

\

其中比較重要的三個類的PhoneWindow,DecorView和ViewRootImpl。PhoneWindow和ViewRootImpl都包含了mDecor成員,它類型為DecorView,描述了Activity的根視圖。它也是ViewRootimpl和PhoneWindow間的樞紐。類似的,PhoneWindow父類中的Callback是PhoneWindow與Activity的樞紐,而ViewRootImpl::W是ViewRootImpl和WMS間的樞紐。DecorView依次繼承自FrameLayout,ViewGroup和View,因此它本質上是一個View,只是它是Activity最根部的View,它下面可能有很多Subview。DecorView描述App窗口視圖,而它的更新是由ViewRootImpl來控制的。粗糙點說的話,如果用MVC模型來說的話,前者是View,後者是Controller。ViewRootImpl中的內嵌類W就是給WMS通信的接口。W的聲明中有兩個成員變量:mViewAncestor和mWindowSession,它一頭連著App端的ViewRootImpl,一頭連著WMS中的Session,且實現了IWindow的接口。意味著它是App和WMS的橋梁,是WMS用來回調App端,讓ViewRootImpl做事用的。舉例來說,dispatchAppVisibility()的流程就是經過它來完成的:WMS ->ViewRootImpl::W->ViewRootHandler->handleAppVisibility()->scheduleTraversals()。

Activity創建完後需要attach到主線程上。在attach()過程中會創建Window(實際是PhoneWindow),然後把PhoneWindow中的mCallback設為Activity。在PhoneWindow中兩個關鍵內嵌類Callback和DecorView,Callback連接了Activity,DecorView連接了ViewRootImpl。這樣,當ViewRootImpl有事件傳來時,便可以沿著ViewRootImpl->DecorView->Window.Callback->Activity這條路來通知Activity。如按鍵事件就是通過這條路來傳達的。

Android裡還可以找到很多這種使用內嵌類來實現遠端代理的例子,這種設計使得系統滿足最小隔離原則,Client端該用到哪些接口就暴露哪些接口。注意這種類的函數都是跑在Binder線程中的,所以其中不能調用非線程安全的函數,也不能直接操作UI控件,所以一般都是往主線程消息隊列裡丟一個消息讓其異步執行。

接下來,看下一個Activity的啟動過程。以Launcher中啟動一個App為例,比如在Launcher中我們點了一個圖標啟動一個App的Activity,Launcher裡會執行:

Intent intent = new Intent("xxx");

startActivity(intent);

接下來的大體流程如下:

\

圖比較大,抓大放小,看清裡面的主要脈絡即可,App與AMS交互流程主要分以下幾步:

1. 原App通知AMS要起一個新Activity。

2. AMS創建相應數據結構,然後通知WMS創建相應數據結構,再通知原Activity暫停。

3. 原Activity暫停後通知AMS。

4. AMS創建新App進程,通知WMS新App可見,再通知App創建Activity等相應數據結構。

流程上我們可以總結出模塊間的異步工作模式:當一個模塊要求另一個模塊做特定任務時,一般是先調用目標模塊的scheduleXXX(),這時目標模塊的Binder線程只是向主線程發起一個異步請求,然後對方主線程在消息隊列中被喚醒處理,執行處理函數handleXXX()。另外我們也注意到很多函數都是帶有Locked後綴。這說明出來混,一定要申明自己是不是線程安全的。

為了使系統中的策略更加靈活,容易替換,系統使用了一些設計模式將之從其它邏輯中解耦。如IPolicy是一個工廠類接口,Policy為它的具體實現類。它負責創建一系列策略相關的對象,如makeNewWindow()創建PhoneWindow等。同時PolicyManager還使用了Strategy模式將Policy包裝起來,這為策略的替換提供了便利,也使運行時更換策略成為可能。

\

雖然目前為止貌似只有一種針對“Phone”的策略,所以還沒看到這樣設計的好處。但是,一方面,也許將來在多合一的移動設備上,筆記本,平板什麼的可以切換,那麼Policy自然也需要動態切換。Android裡還有很多把這種Policy單獨拎出來的例子,如WindowManagerPolicy類。另一方面,Android作為一個框架,需要讓各個廠商把Android用到自己的平台上更加容易適配。將來如果作為眼鏡,車載,智能家電等等嵌入式設備的統一平台,如果將Policy與其它模塊緊耦合,那這些個平台上的代碼就會差異越來越大,越來越難維護。

下面以類圖的方式具體看下各模塊之間的通信關系:

\


圖中App和AMS的交互通過Binder,使用了代理模式。從App調用AMS是通過ActivityManagerProxy代理對象,它是本地對象ActivityManagerNative在App端的代理,實現了IActivityManager接口,提供了startActivity()這樣的AMS服務函數。而ActivityManagerNative的實現其實就是AMS本身。而從AMS調用App端用的是ApplicationThreadProxy代理對象,它實現了IApplicationThread接口,其對應的實現是ApplicationThreadNative本地對象,存在於App端,ApplicationThread是其實現類。AMS可以通過它來向App發出如scheduleXXX這些個異步消息。

Activity在AMS中的對應物是ActivityRecord,在WMS中對應物為AppWindowToken。ActivityRecord::Token可以看作ActivityRecord的一個遠端句柄,在WMS和App中分別存於AppWindowToken和ActivityClientRecord之中。ActivityThread中的mActivities存放了遠程ActivityRecord::Token到本地ActivityClientRecord的映射,由於這個Token是全局唯一的,所以還可以用來作為HashMap的Key。ActivityRecord::Token實現了IApplicationToken。當WMS要通知AMS窗口變化時,就是用的這個接口。

新啟動的Activity的創建初始化主要是在handleLaunchActivity()中完成的。handleLaunchActivity()的作用是加載指定的Activity並運行。這其中在App端主要是創建ActivityThread,Activity,PhoneWindow,DecorView等對象,並調用Activity生命周期中的onCreate(),onResume()函數執行用戶邏輯。正常點的App裡onCreate裡會調用setContentView設置主視圖。setContentView()裡主要是調用了installDecor(),其中會設置窗口的通用元素,如title, action bar之類,還會把xml文件inflate成布局對象。

\

可以看到這時創建了DecorView,這便是Activity的主窗口的頂層視圖。DecorView創建好後,handleResumeActivity()中會將它添加到WMS中去。當App向WMS添加窗口時,會調用WindowManagerImpl的addView()。注意WindowManagerImpl中的addView()函數和ViewGroup裡的addView()函數完全不一樣,後者是將View加入到本地的View hierarchy中去,和WMS沒有關系。addView()的流程如下:

\

首先通過WindowManagerImpl的addView()會創建對應的ViewRootImpl,然後ViewRootImpl申請WMS得到Session(如果是App第一個Activity會新建),其接口為IWindowSession,然後ViewRootImpl通過addToDisplay()把自己的ViewRootImpl::W(實現了IWindow接口)注冊給WMS。這樣,雙方都有了對方的接口,WMS中的Session注冊到WindowManagerGlobal的成員WindowSession中,ViewRootImpl:W注冊到WindowState中的成員mClient中。前者是為了App改變View結構時請求WMS為其更新布局。後者代表了App端的一個添加到WMS中的View,每一個像這樣通過WindowManager接口中addView()添加的窗口都有一個對應的ViewRootImpl,也有一個相應的ViewRootImpl::W。它可以理解為是ViewRootImpl中暴露給WMS的接口,這樣WMS可以通過這個接口和App端通信。Session建立好後,接下來就是通過ViewRootImpl的setView將ViewRootImpl中的W注冊到WMS中,WMS會創建相應數據結構,並將其插入內部維護的窗口堆棧,還會與SF建立Session以備將來為之創建Surface。addView()執行完後這個Activity的主視圖就正式對WMS可見了。總結來說,addView()的工作主要包括創建ViewRootImpl,和遠程WMS建立Session,並將當前視圖注冊到WMS這幾步。可以看到App端通過WindowManagerGlobal調用addView(),調用鏈到WMS就變成addWindow(),概念發生了改變,這也印證上面提到的App端和WMS端的Window概念不一樣的說法。

從以上Activity啟動的整個流程可以看到,窗口的添加和管理需要AMS和WMS兩個Service的配合。下面看看AMS與WMS的主要作用和結構。

AMSActivityManagerService

Activity的管理者。其實除了Activity,AMS也管Service等組件信息,另外AMS還管理Process信息。下面是AMS幾個關鍵數據結構:

ActivityRecord:描述單個Activity,Activity堆棧中的基本單元。

ActivityRecord::Token:對應ActivityRecord的IBinder對象,可以看作遠程對象的本地句柄。可用於LPC,又可用來作映射中的unique ID,經常是兩用的。

ProcessRecord:描述一個App進程,包含了該進程中的Activity和Service列表。

TaskRecord:TaskRecord中的mActivities是ActivityRecord的列表,它們是按照歷史順序排序的。

ActivityStack:Activity堆棧,其中的ActivityRecord是通過TaskRecord這一層間接地被管理著。

ActivityStackSupervisor:ActivityStackSupervisor是ActivityStack的總管。4.4中默認引入了兩個ActivityStack,一個叫Home stack,放Launcher和systemui,id為0;另一個是Applicationstack,放App的Activity,id可能是任意值。定義如下:

137    /** The stack containing the launcher app*/
138    private ActivityStack mHomeStack;
 
145    /** All the non-launcher stacks */
146    private ArrayList mStacks = new ArrayList();

系統中的Activity堆棧信息可以通過dumpsys activity命令查看:

$ adb shell am stackboxes
Box id=3 weight=0.0vertical=false bounds=[0,0][1280,736]
Stack=
  Stack id=3 bounds=[0,0][1280,736]
    taskId=6:com.example.android.apis/com.example.android.apis.ApiDemos
    taskId=7:com.android.camera/com.android.camera.Camera
 
Box id=0 weight=0.0vertical=false bounds=[0,0][1280,736]
Stack=
  Stack id=0 bounds=[0,0][1280,736]
    taskId=3:com.android.launcher/com.android.launcher2.Launcher


從dump信息可以看出,這大致是一個層級結構,從上到下依次是Stack->Task->Activity的結構。Stack放在ActivityStackSupervisor中的mStacks。它是一個列表,元素類型為ActivityStack。因此,基本元素ActivityRecord是以層級的結構被AMS管理起來的:

\

為什麼引入了Task的概念呢?首先看下Task的官方定義“A task (from the activity that started it to the next task activity)defines an atomic group of activities that the user can move to.”。Task是為了完成一個功能的一系列相關的有序Activity集合,可以理解為用戶與App之間對於特定功能的一次會話。一個Task中的Activity可以來自不同的App,比如在郵件App中需要看圖片附件,然後會開imageview的Activity來顯示它。根據不用的業務邏輯,我們會在啟動Activity的Intent中設不同的FLAG(FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_MULTIPLE_TASK等),這些FLAG的處理中會對Task的處理,進而對Activity的調度產生影響。具體的FLAG可參見/frameworks/base/core/java/android/content/Intent.java。關於Task的官方介紹http://developer.android.com/guide/components/tasks-and-back-stack.html。

總得來說,上面這些個數據結構之間的關系如下\


WMSWindowManagerService

窗口的管理者。與AMS不同,一些高層的App中的概念,如進程等,WMS是不care的。因為WMS只對窗口進行管理,哪個進程的它不關心。像Activity這些概念在WMS仍然有,因為Activity對窗口的管理會產生影響。

WMS主要責任是維護窗口堆棧,計算每個窗口的layer信息交給SF,替App申請和調整繪圖Surface,當窗口顯示狀態變化了還要通知其它模塊,另外還要處理系統輸入。所以說,WMS可能是與其它模塊交互最多的模塊之一了。它與AMS,App,SF及Input等模塊都交集。說到窗口管理,首先看一下Android中有哪些窗口。Android中大體有以下幾種窗口類型:1.應用窗口,一般來說就是Activity的主窗口。但也有些情況App沒有Activity,直接把自定義的View添加到WMS中,比如浮動窗口。2.子窗口,需要有一個父窗口,如Context Menu,Option Menu,Popup Window和Dialog等。3.系統窗口,如狀態欄,鎖屏窗口,輸入法窗口,壁紙窗口和Toast之流,由系統創建的,不依賴於父窗口。

WMS中涉及到的主要數據結構有這麼幾個:

WindowState:WMS中最基本的元素,描述WMS中的一個窗口。它既可以是由App添加過來的View,也可以是系統創建的系統窗口。mAttrs為WindowManager.LayoutParams類型,描述布局參數。mClient為IWindow類型,也就是App端的ViewRootImpl::W。為了查找方便,WMS中的mWindowMap保存了IWindow到WindowState的映射,mTokenMap保存了IApplicationToken到WindowToken的映射。

Session向App提供IWindowSession接口讓其可以和WMS通信。每個App在WMS有一個Session對象,App就是通過這個Session來向WMS發出窗口管理申請的。命令dumpsys window sessions可以查看系統中的Session:

WINDOW MANAGERSESSIONS (dumpsys window sessions)
  Session Session{b32d7d68 1404:u0a10008}:
    mNumWindow=1 mClientDead=falsemSurfaceSession=android.view.SurfaceSession@b31adc20
  Session Session{b32dd278 1326:u0a10007}:
    mNumWindow=5 mClientDead=falsemSurfaceSession=android.view.SurfaceSession@b327b348
  Session Session{b3290f68 1275:1000}:
    mNumWindow=1 mClientDead=falsemSurfaceSession=android.view.SurfaceSession@b30a3890

SurfaceSession:WMS和SF之間的會話。每個App會在WMS中有一個對應的SurfaceSession,也會有一個相應的SurfaceComposerClient。用於向SF申請和設置圖形緩沖區等。

WindowToken: 描述WM中一組相關的窗口,這些Window對應的WindowState放在其成員變量windows裡。其主要繼承類AppWindowToken,它是針對App的WindowToken結構。WindowState中的mAppToken指向所屬的AppWindowToken,如果是系統窗口,mAppToken為空,mToken指向WindowToken對象。

命令dumpsys window tokens用於查看WindowToken和AppWindowToken信息:

WINDOW MANAGERTOKENS (dumpsys window tokens)
  All tokens:
  WindowToken{b32921e8 null}:
    windows=[Window{b333cd40 u0 SearchPanel},Window{b328d920 u0 Keyguard}, Window{b32961d8 u0 NavigationBar},Window{b32c6aa0 u0 StatusBar}, Window{b3292288 u0 KeyguardScrim}]
    windowType=-1 hidden=false hasVisible=true
  WindowToken{b3260980android.os.Binder@b3269d60}:
    windows=[Window{b325c180 u0com.android.systemui.ImageWallpaper}]
    windowType=2013 hidden=falsehasVisible=true
  AppWindowToken{b322a358 token=Token{b3287ea0ActivityRecord{b3287c28 u0 com.android.launcher/com.android.launcher2.Launchert1}}}:
    windows=[Window{b328b0c0 u0com.android.launcher/com.android.launcher2.Launcher}]
    windowType=2 hidden=false hasVisible=true
    app=true
    allAppWindows=[Window{b328b0c0 u0com.android.launcher/com.android.launcher2.Launcher}]
    groupId=1 appFullscreen=truerequestedOrientation=-1
    hiddenRequested=false clientHidden=falsewillBeHidden=false reportedDrawn=true reportedVisible=true
    numInterestingWindows=1 numDrawnWindows=1inPendingTransaction=false allDrawn=true (animator=true)
    startingData=null removed=falsefirstWindowDrawn=true
  WindowToken{b32b81c0android.os.Binder@b3228950}:
    windows=[]
    windowType=2011 hidden=falsehasVisible=false
 
  Wallpaper tokens:
  Wallpaper #0 WindowToken{b3260980android.os.Binder@b3269d60}:
    windows=[Window{b325c180 u0com.android.systemui.ImageWallpaper}]
    windowType=2013 hidden=falsehasVisible=true

AppWindowToken每個App的Activity對應一個AppWindowToken。其中的appToken為IApplicationToken類型,連接著對應的AMS中的ActivityRecord::Token對象,有了它就可以順著AppWindowToken找到AMS中相應的ActivityRecord。其中allAppWindows是一個無序的列表,包含該Activity中所有的窗口。用dumpsys window display可以查看z-ordered的AppWindowToken列表:

  Application tokens in Z order:
  App #4 AppWindowToken{b31c2128token=Token{b3235c98 ActivityRecord{b324c8a0 u0com.example.android.apis/.view.PopupMenu1 t9}}}:
    windows=[Window{b32a5eb8 u0com.example.android.apis/com.example.android.apis.view.PopupMenu1}]
    windowType=2 hidden=false hasVisible=true
    app=true
   allAppWindows=[Window{b32a5eb8 u0com.example.android.apis/com.example.android.apis.view.PopupMenu1},Window{b32eb6f0 u0 PopupWindow:b2ff5368}]
    groupId=9 appFullscreen=truerequestedOrientation=-1
    hiddenRequested=false clientHidden=falsewillBeHidden=false reportedDrawn=true reportedVisible=true
    numInterestingWindows=2 numDrawnWindows=2inPendingTransaction=false allDrawn=true (animator=true)
    startingData=null removed=falsefirstWindowDrawn=true
  App #3 AppWindowToken{b3429e18token=Token{b31c5e58 ActivityRecord{b31e8ff0 u0com.example.android.apis/.ApiDemos t9}}}:
    windows=[Window{b32a39f8 u0com.example.android.apis/com.example.android.apis.ApiDemos}]
    windowType=2 hidden=true hasVisible=true
    app=true
    allAppWindows=[Window{b32a39f8 u0com.example.android.apis/com.example.android.apis.ApiDemos}]
    groupId=9 appFullscreen=truerequestedOrientation=-1
    hiddenRequested=true clientHidden=truewillBeHidden=false reportedDrawn=false reportedVisible=false
    numInterestingWindows=1 numDrawnWindows=1inPendingTransaction=false allDrawn=true (animator=true)
    startingData=null removed=falsefirstWindowDrawn=true
  App #2 AppWindowToken{b32ccde0token=Token{b333d128 ActivityRecord{b32dcf10 u0com.example.android.apis/.ApiDemos t9}}}:
    windows=[Window{b320fd28 u0com.example.android.apis/com.example.android.apis.ApiDemos}]
    windowType=2 hidden=true hasVisible=true
    app=true
    allAppWindows=[Window{b320fd28 u0com.example.android.apis/com.example.android.apis.ApiDemos}]
    groupId=9 appFullscreen=truerequestedOrientation=-1
    hiddenRequested=true clientHidden=truewillBeHidden=false reportedDrawn=false reportedVisible=false
    numInterestingWindows=1 numDrawnWindows=1inPendingTransaction=false allDrawn=true (animator=true)
    startingData=null removed=falsefirstWindowDrawn=true
  App #1 AppWindowToken{b321ff58token=Token{b321d860 ActivityRecord{b321d990 u0com.android.launcher/com.android.launcher2.Launcher t1}}}:
    windows=[Window{b3268018 u0com.android.launcher/com.android.launcher2.Launcher}]
    windowType=2 hidden=true hasVisible=true
    app=true
    allAppWindows=[Window{b3268018 u0com.android.launcher/com.android.launcher2.Launcher}]
    groupId=1 appFullscreen=truerequestedOrientation=-1
    hiddenRequested=true clientHidden=truewillBeHidden=false reportedDrawn=false reportedVisible=false
    numInterestingWindows=1 numDrawnWindows=1inPendingTransaction=false allDrawn=true (animator=true)
    startingData=null removed=falsefirstWindowDrawn=true

注意AppWindowToken是對應Activity的,WindowState是對應窗口的。所以AppWindowToken和WindowState是1:n的關系。舉例來說,上面第一項AppWindowToken,它包含了PopupWindow子窗口,所以有對應兩個WindowState。一般地說,一個Activity可能包含多個窗口,如啟動窗口,PopupWindow等,這些窗口在WMS就會組織在一個AppWindowToken中。AppWindowToken及WindowState間的從屬結構及WindowState間的父子結構可以通過以下成員表示。

\

Task上面提到AppWindowToken保存了屬於它的WindowState的有序列表,而它本身也作為一個列表被管理在TaskStack中的mTasks成員中,並且是按歷史順序存放的,最老的Task在最底下。結合前面的AppWindowToken和WindowState之間的關系,可以了解到它們是以這樣一個層級的關系組織起來的:

\

這個結構是不是很眼熟,AMS裡也有類似的結構。WMS裡的TaskStack,對應前面AMS中的ActivityStack,這兩者及其子結構會保持同步。從中我們可以發現,WMS和AMS中的數據結構是有對應關系的,如AMS中的TaskRecord和WMS中的Task,AMS中的ActivityRecord和WMS中的AppWindowToken。另外WMS中TaskStack的mTasks需要和AMS中ActivityStack的mTaskHistory順序保持一致。

\

DisplayContent:表示一個顯示設備上的內容,這個顯示設備可以是外接顯示屏,也可以是虛擬顯示屏。其中mWindows是一個WindowState的有序(Z-ordered,底部最先)列表。mStackBoxes包含了若干個StackBox,其中一個為HomeStack,另一個是App的StackBox。所有的StackBox被組織成二叉樹,StackBox是其中的節點,其中有三個重要成員變量,mFirst和mSecond指向左和右子結點(也是StackBox),StackBox的成員mStack才是我們真正關心的東西-TaskStack。可以看到,為了要把TaskStack存成樹的結構,需要一個容器,這個容器就是StackBox。DisplayContent,StackBox和TaskStack的關系如下:

\

StackBox信息可以用am stack boxes或dumpsys window displays命令查看:

$ adb shell am stackboxes
WARNING: linker:libdvm.so has text relocations. This is wasting memory and is a security risk.Please fix.
Box id=2 weight=0.0vertical=false bounds=[0,33][800,1216]
Stack=
  Stack id=2 bounds=[0,33][800,1216]
    taskId=3:com.android.contacts/com.android.contacts.activities.PeopleActivity
 
Box id=0 weight=0.0vertical=false bounds=[0,33][800,1216]
Stack=
  Stack id=0 bounds=[0,33][800,1216]
    taskId=1:com.android.launcher/com.android.launcher2.Launcher
      mStackId=2
      {taskId=3appTokens=[AppWindowToken{b3332498 token=Token{b33006c0 ActivityRecord{b32ecbb0u0 com.android.contacts/.activities.PeopleActivity t3}}}]}

上面是正常情況下的,用am stack create命令可以創建分屏窗口(詳見http://androidinternalsblog.blogspot.com/2014/03/split-screens-in-android-exist.html),如:

$ adb shell am stackcreate 7 3 0 0.5
createStack returnednew stackId=4

然後再查看stack信息就變成了這樣:

$ adb shell am stackboxes
Box id=3 weight=0.5vertical=false bounds=[0,0][1280,736]
First child=
  Box id=4 weight=0.0 vertical=falsebounds=[0,0][640,736]
  Stack=
    Stack id=4 bounds=[0,0][640,736]
      taskId=7:com.android.camera/com.android.camera.Camera
Second child=
  Box id=5 weight=0.0 vertical=falsebounds=[640,0][1280,736]
  Stack=
    Stack id=3 bounds=[640,0][1280,736]
      taskId=6:com.example.android.apis/com.example.android.apis.ApiDemos
 
Box id=0 weight=0.0vertical=false bounds=[0,0][1280,736]
Stack=
  Stack id=0 bounds=[0,0][1280,736]
    taskId=3:com.android.launcher/com.android.launcher2.Launcher

可見,在分屏情況下,這個結構以二叉樹的形式分裂,形成這樣的結構:

\

除了TaskStack,DisplayContent中的成員mTaskHistory也包含了一個有序的Task列表。結合上面幾個概念,可以得到下面的關系:

\

邏輯上,一個Task(Task)可以包含多個Activity(對應AppWindowToken),每個Activity可以包含多個窗口(對應WindowState),而每個窗口都是可能被放在任意一個顯示屏上的(想象一個Windows操作系統中外接顯示器的情況),因此就有了上面這個結構。從這個結構可以看出,WMS的主要任務之一就是維護各窗口的Z-order信息。Z軸可看作是屏幕法向量方向上的坐標軸,值越大的層意味著離用戶越近,會把值小的窗口給蓋住。一方面,WMS需要知道各窗口的遮擋關系來做layout和分配釋放Surface,另一方面,這個Z-order信息會轉化為窗口對應Surface的layer屬性輸出到SF,指導SF的渲染。

那麼,這個列表是怎麼管理的呢?我們知道,每個窗口在WMS都有對應的WindowState,因此,本質上我們需要維護一個Z-order排序的WindowState列表。首先,TaskStack中包含了歷史序的Task,每個Task又包含了Z-ordered的AppWindowToken,AppWindowToken的成員windows又包含了一個Z-ordered的WindowState列表。前面提到過,一個AppWindowToken對應AMS中的一個ActivityRecord,因此這個列表包含了這個Activity中的所有窗口,子窗口,開始窗口等。另一方面,DisplayContent中也有一個成員windows,指向一個Z-ordered的WindowState列表,它描述的是單個顯示屏上的窗口集合。在添加窗口時會通過addAppWindowToListLocked()函數往這個窗口堆棧插入元素。由於插入過程要考慮子窗口,開始窗口等的偏移量,往DisplayContent的windows插入元素時需考慮WindowToken中的windows列表。得到DisplayContent中的windows列表後,之後會調用assignLayersLocked()來根據這個Z-order列表信息得到每個窗口的layer值。WindowState的列表可以用dumpsys window windows命令查看:

WINDOW MANAGERWINDOWS (dumpsys window windows)
  Window #7 Window{b32b2110 u0 SearchPanel}:
    mDisplayId=0 mSession=Session{b32369b81326:u0a10007} mClient=android.os.BinderProxy@b3222788
    mOwnerUid=10007 mShowToOwnerOnly=falsepackage=com.android.systemui appop=NONE
    mAttrs=WM.LayoutParams{(0,0)(fillxfill)gr=#800053 sim=#31 ty=2024 fl=#1820100 fmt=-3 wanim=0x10301f5}
    Requested w=800 h=1216 mLayoutSeq=37
    mHasSurface=falsemShownFrame=[0.0,0.0][0.0,0.0] isReadyForDisplay()=false
    WindowStateAnimator{b32f06d8 SearchPanel}:
      mShownAlpha=0.0 mAlpha=1.0 mLastAlpha=0.0
...
  Window #2 Window{b3282e18 u0com.example.android.apis/com.example.android.apis.ApiDemos}:
    mDisplayId=0 mSession=Session{b32cfba03137:u0a10045} mClient=android.os.BinderProxy@b31faaf8
    mOwnerUid=10045 mShowToOwnerOnly=truepackage=com.example.android.apis appop=NONE
    mAttrs=WM.LayoutParams{(0,0)(fillxfill)sim=#110 ty=1 fl=#1810100 pfl=0x8 wanim=0x10302f5}
    Requested w=800 h=1216 mLayoutSeq=82
    mHasSurface=truemShownFrame=[0.0,0.0][800.0,1216.0] isReadyForDisplay()=true
    WindowStateAnimator{b32df438com.example.android.apis/com.example.android.apis.ApiDemos}:
      Surface: shown=true layer=21010 alpha=1.0rect=(0.0,0.0) 800.0 x 1216.0
...

總結一下,WMS服務端的類結構圖:

\


設計中比較靈活的一個地方是其中的WindowManagerPolicy這個類,它采用了Strategy模式,將策略相關的部分抽象出來,用PhoneWindowManager實現。它也是前面提到的Policy工廠模式的一部分。雖然現在PhoneWindowManager是唯一的繼承類,這種結構看似可有可無,但如果以後廠商要加其它的策略,或更改已有策略,這種設計就能夠提供良好的靈活性。

以上就是AMS和WMS的大體框架。粗糙地說,AMS管理Activity,Service和Process等信息,WMS管理應用和系統窗口。這兩者既有聯系又有很大不同,Activity一般有窗口,Service可以有也可以沒有窗口,而窗口不一定非要對應Activity或者Service。只是很多時候一個Activity中就一個頂層視圖,對應WMS一個窗口,所以會給人一一對應的錯覺,但這兩者其實沒有直接關系。這就造就了AMS,WMS兩大模塊,雖然其中數據結構有很多是保持同步的,但是設計上讓它們分開,各司其職,異步工作。從窗口管理的流程來說,App負責視圖樹的管理和業務邏輯。AMS管理和調度所有App中的組件,通知WMS組件的狀態信息。WMS幫App到SF申請和調整Surface,同時計算維護窗口的布局,z-order等信息,另外當顯示狀態發生變化時,WMS還要通知App作出調整。SF從WMS拿到各窗口對應Surface的屬性和layer信息,同時從App拿到渲染好的圖形緩沖區,進行進一步的合並渲染,放入framebuffer,最後用戶就能在屏幕上看到App的窗口了。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved