频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
Android 4.4(KitKat)窗口管理子系统 - 体系框架
2014-07-15 11:00:44         来源:世事难料,保持低调  
收藏   我要投稿

窗口管理系统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的官方介绍https://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命令可以创建分屏窗口(详见https://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的窗口了。


点击复制链接 与好友分享!回本站首页
相关TAG标签 子系统 框架 体系
上一篇:解决:Android4.3锁屏界面Emergency calls only - China Unicom与EMERGENCY CALL语义重复
下一篇:Android Content Provider在应用程序之间共享数据的原理分析
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站