原文地址:http://blog.csdn.net/jinzhuojun/article/details/37737439


窗口管理系统是Android中的主要子系统之一,它涉及到App中组件的管理,系统和应用窗口的管理和绘制等工作。由于其涉及模块众多,且与用户体验密切相关,所以它也是Android当中最为复杂的子系统之一。一个App从启动到主窗口显示出来,需要AppActivityManagerServiceAMS),WindowManagerServiceWMS),SurfaceFlingerSF)等几个模块相互合作。App负责业务逻辑,绘制自己的视图;AMS管理组件、进程信息和Activity的堆栈及状态等等;WMS管理Activity对应的窗口及子窗口,还有系统窗口等;SF用于管理图形缓冲区,将App绘制的东西合成渲染在屏幕上。下面分几个部分进行分析。

窗口管理系统的主要框架及各模块之间的通讯接口大体如下:


基于Binder的本地过程调用(LPC)让Android的模块间耦合度更低,结构更加清晰。每个模块各司其职,并向其它模块提供接口。进程和uid这些Linux中的机制对这些模块提供了天然的保护,使得系统更加鲁棒。模块之间经常使用C/S的结构,而Service本身也可能是使用其它ServiceClient。举例来说,如果Service的实现叫XXXManagerService,那一般它对Client提供接口IXXXManager,然后Client要用Service的时候便会申请一个叫BpXXXManager的代理对象,它是远端BnXXXManager本地对象在Client端的代理。代理对象BpXXManager实现了IXXXManager的所有接口,只不过里面的函数都是壳子,只负责参数的准备,然后就调用远端对象去执行。远端的的BnXXXManager对象及其继承类是真正做事的,BnXXXManager继承自IXXXManager.Stub抽象类,实现了IXXXManager接口。Stub就如其名字一样,是BnXXXManager的继承类在BnXXXManager中的“钩子”。通过调用这些接口便可以调用到远端的Service功能了。概念上类似远程gdb调试,host机上的gdbguest上的gdbserver相连以后,在host上敲命令会让gdbserver去执行,但感觉就像是在host本地执行一样。这儿的gdbserver就提供了类似于Stub的功能。

这种远程调用模型的建立过程一般是分层次的。比如WindowManagerGlobal会与WMS进行连接,ViewRootImpl会与WMS中的Session进行连接,高层先与高层通信,同时帮助建立低层间的通信,然后低层与低层直接通信。打个比方,张三是A部门的员工,他想要和B部门合作搞一个活动,他一般不会直接冲过去B部门挨个问的。所以他先和自己的主管李四说,我要和B部门合作,于是李四找到B部门的主管王五,说你出个人吧。于是王五和赵六说,你负责这事儿吧,并告诉了A部门主管李四。李四再告诉下属张三,赵六是B部门接口人,你以后直接和他联系吧。于是张三和赵六以后就直接联系了。如果合作中有需要超越自己权限的操作,他们再向各自的主管申请。比如AppWMS的连接,首先会建立一个SessionWMS,之后就会通过IWindowSession接口与WMS中的Session直接通信。还有例如WMSSF先创建SurfaceSession,其中会创建SurfaceComposerClient,访问SurfaceComposerClient时会在SF中创建Client与之对应,这个Client实现了ISurfaceComposerClient接口,之后SurfaceComposerClient会通过该接口与SF中的Client直接通信。

看代码过程中,各个对象间的数量及对应关系经常让人混淆,下面列举了在一般情况下各对象之间的实体关系图。其中标色的是相应子系统中比较基础核心的类。


要注意的几点:1. App中可以没有Activity,也可以没有PhoneWindowDecorView,比如一个显示浮动窗口的Service2. Task中的Activity可以来自不同进程,比如App运行过程中打开相机App拍照。3. WindowState代表WMS中的一个窗口,这和App端的Window类是不一样的,尽管很多时候一个Window类(即PhoneWindow)有一个对应的WindowState,但那不是绝对的。一个ActivityWMS中有对应的AppWindowToken,一个AppWindowToken又可以包含多个WindowState,因为除了主窗口外,还可能有子窗口和启动窗口。此外对于系统窗口,WindowState还可能不对应AppWindowToken4.这里的Application指的是App端的一个进程,它不同于AndroidManifest.xml中的<application>标签。后者是配置文件中对组件的管理者,它和进程之间没有本质关系,通过android:process标签可以让同一个<application>下的组件跑在多个进程,也可以让多个<application>中的组件跑在同一个进程。所以如果是<application>定义的Application的话和ProcessRecord就是m:n的关系了。以下谈到Application都是指一个App的进程。


首先分析下App端的结构。移动平台一般显示区域有限,要完成一个工作往往不是一屏内容中能搞定的,所以Android中有了Activity的概念,让用户可以把相关的子内容放到单独的Activity中,然后通过IntentActivity间跳转。类似于浏览网页,点击链接跳转到另一个网页。这些同一交互过程中的一系列Activity成为一个Task。这些Activity运行在主线程ActivityThread中。Activity要展现出来的主视图是DecorView,它是一棵视图树。ViewRootImpl负责管理这个视图树和与WMS交互,与WMS交互通过WindowManagerImplWindowManagerGlobalDecorView被包含在系统的通用窗口抽象类Window当中。视图对应的图形缓冲区由Surface管理。其中涉及到的主要的类包括下面几个:

Activity:描述一个Activity,它是与用户交互的基本单元。

ActivityThread:每一个App进程有一个主线程,它由ActivityThread描述。它负责这个App进程中各个Activity的调度和执行,以及响应AMS的操作请求等。

ApplicationThreadAMSActivity通过它进行通信。对于AMS而言,ApplicationThread代表了App的主线程。简而言之,它是AMSActivityThread进行交互的接口。注意ActivityThreadApplicationThread之间的关系并不像ActivityApplication。后者的关系是Application中包含了多个Activity,而前者ActivityThreadApplicationThread是同一个东西的两种"View"ApplicationThread是在AMS眼中的ActivityThread

ViewRootImpl:主要责任包括创建Surface,和WMS的交互和App端的UI布局和渲染。同时负责把一些事件发往Activity以便Activity可以截获事件。每一个添加到WMS中的窗口对应一个ViewRootImpl,通过WindowManagerGlobalWMS添加窗口时创建。大多数情况下,它管理Activity顶层视图DecorView。总得来说,它相当于MVC模型中的Controller

ViewRootImpl::W:用于向WMS提供接口,让WMS控制App端的窗口。它可看作是个代理,很多时候会调用ViewRootImpl中的功能。这种内嵌类的用法很多,特别是这种提供接口的代理类,如PhoneWindow::DecorView等。

Instrumentation:官方提供的Hook,主要用于测试。如果只关注窗口管理流程的话可以先无视。

WindowManagerImplActivity中与窗口管理系统通信的代理类,实现类是WindowManagerGlobalWindowManagerGlobalApp中全局的窗口管理模块,因此是个Singleton。其中管理着该App中的ViewRootImplDecorView等结构,以有两个ActivityApp为例:


Window:每个App虽然都可以做得各不相同,但是作为有大量用户交互的系统,窗口之间必须要有统一的交互模式,这样才能减小用户的学习成本。这些共性比如title, action bar的显示和通用按键的处理等等。Window类就抽象了这些共性。另外,它定义了一组CallbackActivity通过实现这些Callback被调用来处理事件。注意要和在WMS中的窗口区分开来,WMS中的窗口更像是App端的View

PhoneWindowPhoneWindowWindow类的唯一实现,至少目前是。这样的设计下如果要加其它平台的Window类型更加方便。每个Activity会有一个PhoneWindow,在attachActivityThread时创建,保存在mWindow成员中

Context:运行上下文,ActivityService本质上都是一个ContextContext包含了它们作为运行实体的共性,如启动Activity,绑定Service,处理BroadcastReceiver等等。注意Application也会有ContextActivityContext是对应Activity的,Activity被杀掉(比如转屏后)后就变了。所以要注意如果有生命周期很长的对象有对ActivityContext的引用的话,转屏、返回这种会引起Activity销毁的操作都会引起内存泄露。而ApplicationContext生命周期是和App进程一致的。关于Context的类结构图有下面的形式。Context是抽象类,定义了接口。ContextImplContext的实现类,包含了实现。而ContextWrapperContext的包装类,它把请求delegate给其中的ContextImpl类去完成。ContextThemeWrapperContextWrapper的装饰类,它在ContextWrapper的基础上提供了自定义的主题。这结构初看有点乱,但结合下面的Decorator模式就一目了然了。


Surface:这是在App端管理图形缓冲区的类,其中最重要的是图形缓冲区队列。经由WMSSF中得到IGraphicBufferProducer接口对象BufferQueue后,Surface便可以从该队列中queuedequeue图形缓冲区。SurfaceControlWMS中封装了Surface以及与SF的交互。CanvasSurface从字面意思上很像,但前者其实更确切地说不是“画布”,而是“画家”。Surface中的图形缓冲区才是App的画布。

上面这些基本类之间的主要关系如下:


其中比较重要的三个类的PhoneWindowDecorViewViewRootImplPhoneWindowViewRootImpl都包含了mDecor成员,它类型为DecorView,描述了Activity的根视图。它也是ViewRootimplPhoneWindow间的枢纽。类似的,PhoneWindow父类中的CallbackPhoneWindowActivity的枢纽,而ViewRootImpl::WViewRootImplWMS间的枢纽。DecorView依次继承自FrameLayoutViewGroupView,因此它本质上是一个View,只是它是Activity最根部的View,它下面可能有很多SubviewDecorView描述App窗口视图,而它的更新是由ViewRootImpl来控制的。粗糙点说的话,如果用MVC模型来说的话,前者是View,后者是ControllerViewRootImpl中的内嵌类W就是给WMS通信的接口。W的声明中有两个成员变量:mViewAncestormWindowSession,它一头连着App端的ViewRootImpl,一头连着WMS中的Session,且实现了IWindow的接口。意味着它是AppWMS的桥梁,是WMS用来回调App端,让ViewRootImpl做事用的。举例来说,dispatchAppVisibility()的流程就是经过它来完成的:WMS ->ViewRootImpl::W->ViewRootHandler->handleAppVisibility()->scheduleTraversals()

Activity创建完后需要attach到主线程上。在attach()过程中会创建Window(实际是PhoneWindow),然后把PhoneWindow中的mCallback设为Activity。在PhoneWindow中两个关键内嵌类CallbackDecorViewCallback连接了ActivityDecorView连接了ViewRootImpl。这样,当ViewRootImpl有事件传来时,便可以沿着ViewRootImpl->DecorView->Window.Callback->Activity这条路来通知Activity。如按键事件就是通过这条路来传达的。

Android里还可以找到很多这种使用内嵌类来实现远端代理的例子,这种设计使得系统满足最小隔离原则,Client端该用到哪些接口就暴露哪些接口。注意这种类的函数都是跑在Binder线程中的,所以其中不能调用非线程安全的函数,也不能直接操作UI控件,所以一般都是往主线程消息队列里丢一个消息让其异步执行。

接下来,看下一个Activity的启动过程。以Launcher中启动一个App为例,比如在Launcher中我们点了一个图标启动一个AppActivityLauncher里会执行:

Intent intent = new Intent("xxx");

startActivity(intent);

接下来的大体流程如下:


图比较大,抓大放小,看清里面的主要脉络即可,AppAMS交互流程主要分以下几步:

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

2.AMS创建相应数据结构,然后通知WMS创建相应数据结构,再通知原Activity暂停。

3. 原Activity暂停后通知AMS

4.AMS创建新App进程,新App创建好后attach到AMS。AMS再通知新App创建Activity等相应数据结构。

流程上我们可以总结出模块间的异步工作模式:当一个模块要求另一个模块做特定任务时,一般是先调用目标模块的scheduleXXX(),这时目标模块的Binder线程只是向主线程发起一个异步请求,然后对方主线程在消息队列中被唤醒处理,执行处理函数handleXXX()。另外我们也注意到很多函数都是带有Locked后缀。这说明出来混,一定要申明自己是不是线程安全的。

为了使系统中的策略更加灵活,容易替换,系统使用了一些设计模式将之从其它逻辑中解耦。如IPolicy是一个工厂类接口,Policy为它的具体实现类。它负责创建一系列策略相关的对象,如makeNewWindow()创建PhoneWindow等。同时PolicyManager还使用了Strategy模式将Policy包装起来,这为策略的替换提供了便利,也使运行时更换策略成为可能。


虽然目前为止貌似只有一种针对“Phone”的策略,所以还没看到这样设计的好处。但是,一方面,也许将来在多合一的移动设备上,笔记本,平板什么的可以切换,那么Policy自然也需要动态切换。Android里还有很多把这种Policy单独拎出来的例子,如WindowManagerPolicy类。另一方面,Android作为一个框架,需要让各个厂商把Android用到自己的平台上更加容易适配。将来如果作为眼镜,车载,智能家电等等嵌入式设备的统一平台,如果将Policy与其它模块紧耦合,那这些个平台上的代码就会差异越来越大,越来越难维护。

下面以类图的方式具体看下各模块之间的通信关系:



图中AppAMS的交互通过Binder,使用了代理模式。从App调用AMS是通过ActivityManagerProxy代理对象,它是本地对象ActivityManagerNativeApp端的代理,实现了IActivityManager接口,提供了startActivity()这样的AMS服务函数。而ActivityManagerNative的实现其实就是AMS本身。而从AMS调用App端用的是ApplicationThreadProxy代理对象,它实现了IApplicationThread接口,其对应的实现是ApplicationThreadNative本地对象,存在于App端,ApplicationThread是其实现类。AMS可以通过它来向App发出如scheduleXXX这些个异步消息。

ActivityAMS中的对应物是ActivityRecord,在WMS中对应物为AppWindowTokenActivityRecord::Token可以看作ActivityRecord的一个远端句柄,在WMSApp中分别存于AppWindowTokenActivityClientRecord之中。ActivityThread中的mActivities存放了远程ActivityRecord::Token到本地ActivityClientRecord的映射,由于这个Token是全局唯一的,所以还可以用来作为HashMapKeyActivityRecord::Token实现了IApplicationToken。当WMS要通知AMS窗口变化时,就是用的这个接口。

新启动的Activity的创建初始化主要是在handleLaunchActivity()中完成的。handleLaunchActivity()的作用是加载指定的Activity并运行。这其中在App端主要是创建ActivityThreadActivityPhoneWindowDecorView等对象,并调用Activity生命周期中的onCreate()onResume()函数执行用户逻辑。正常点的ApponCreate里会调用setContentView设置主视图。setContentView()里主要是调用了installDecor(),其中会设置窗口的通用元素,如title, action bar之类,还会把xml文件inflate成布局对象。


可以看到这时创建了DecorView,这便是Activity的主窗口的顶层视图。DecorView创建好后,handleResumeActivity()中会将它添加到WMS中去。当AppWMS添加窗口时,会调用WindowManagerImpladdView()。注意WindowManagerImpl中的addView()函数和ViewGroup里的addView()函数完全不一样,后者是将View加入到本地的View hierarchy中去,和WMS没有关系。addView()的流程如下:


首先通过WindowManagerImpladdView()会创建对应的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建立好后,接下来就是通过ViewRootImplsetViewViewRootImpl中的W注册到WMS中,WMS会创建相应数据结构,并将其插入内部维护的窗口堆栈,还会与SF建立Session以备将来为之创建SurfaceaddView()执行完后这个Activity的主视图就正式对WMS可见了。总结来说,addView()的工作主要包括创建ViewRootImpl,和远程WMS建立Session,并将当前视图注册到WMS这几步。可以看到App端通过WindowManagerGlobal调用addView(),调用链到WMS就变成addWindow(),概念发生了改变,这也印证上面提到的App端和WMS端的Window概念不一样的说法。

从以上Activity启动的整个流程可以看到,窗口的添加和管理需要AMSWMS两个Service的配合。下面看看AMSWMS的主要作用和结构。

AMSActivityManagerService

Activity的管理者。其实除了ActivityAMS也管Service等组件信息,另外AMS还管理Process信息。下面是AMS几个关键数据结构:

ActivityRecord:描述单个ActivityActivity堆栈中的基本单元。

ActivityRecord::Token:对应ActivityRecordIBinder对象,可以看作远程对象的本地句柄。可用于LPC,又可用来作映射中的unique ID,经常是两用的。

ProcessRecord:描述一个App进程,包含了该进程中的ActivityService列表。

TaskRecordTaskRecord中的mActivitiesActivityRecord的列表,它们是按照历史顺序排序的。

ActivityStackActivity堆栈,其中的ActivityRecord是通过TaskRecord这一层间接地被管理着。

ActivityStackSupervisorActivityStackSupervisorActivityStack的总管。4.4中默认引入了两个ActivityStack,一个叫Home stack,放Launchersystemuiid0;另一个是Applicationstack,放AppActivityid可能是任意值。定义如下:

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

系统中的Activity堆栈信息可以通过dumpsys activity命令查看:

$ adb shell am stackboxesBox 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中需要看图片附件,然后会开imageviewActivity来显示它。根据不用的业务逻辑,我们会在启动ActivityIntent中设不同的FLAGFLAG_ACTIVITY_NEW_TASKFLAG_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可能是与其它模块交互最多的模块之一了。它与AMSAppSFInput等模块都交集。说到窗口管理,首先看一下Android中有哪些窗口。Android中大体有以下几种窗口类型:1.应用窗口,一般来说就是Activity的主窗口。但也有些情况App没有Activity,直接把自定义的View添加到WMS中,比如浮动窗口。2.子窗口,需要有一个父窗口,如Context MenuOption MenuPopup WindowDialog等。3.系统窗口,如状态栏,锁屏窗口,输入法窗口,壁纸窗口和Toast之流,由系统创建的,不依赖于父窗口。

WMS中涉及到的主要数据结构有这么几个:

WindowStateWMS中最基本的元素,描述WMS中的一个窗口。它既可以是由App添加过来的View,也可以是系统创建的系统窗口。mAttrsWindowManager.LayoutParams类型,描述布局参数。mClientIWindow类型,也就是App端的ViewRootImpl::W。为了查找方便,WMS中的mWindowMap保存了IWindowWindowState的映射,mTokenMap保存了IApplicationTokenWindowToken的映射。

SessionApp提供IWindowSession接口让其可以和WMS通信。AppWMS有一个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

SurfaceSessionWMSSF之间的会话。每个App会在WMS中有一个对应的SurfaceSession,也会有一个相应的SurfaceComposerClient。用于向SF申请和设置图形缓冲区等。

WindowToken:描述WM一组相关的窗口,这些Window对应的WindowState放在其成员变量windows里。其主要继承类AppWindowToken,它是针对AppWindowToken结构。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每个AppActivity对应一个AppWindowToken。其中的appTokenIApplicationToken类型,连接着对应的AMS中的ActivityRecord::Token对象,有了它就可以顺着AppWindowToken找到AMS中相应的ActivityRecord。其中allAppWindows是一个无序的列表,包含该Activity中所有的窗口。用dumpsys window display可以查看z-orderedAppWindowToken列表:

  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是对应窗口的。所以AppWindowTokenWindowState1:n的关系。举例来说,上面第一项AppWindowToken,它包含了PopupWindow子窗口,所以有对应两个WindowState。一般地说,一个Activity可能包含多个窗口,如启动窗口,PopupWindow等,这些窗口在WMS就会组织在一个AppWindowToken中。AppWindowTokenWindowState间的从属结构及WindowState间的父子结构可以通过以下成员表示。


Task上面提到AppWindowToken保存了属于它的WindowState的有序列表,而它本身也作为一个列表被管理在TaskStack中的mTasks成员中,并且是按历史顺序存放的,最老的Task在最底下。结合前面的AppWindowTokenWindowState之间的关系,可以了解到它们是以这样一个层级的关系组织起来的:


这个结构是不是很眼熟,AMS里也有类似的结构。WMS里的TaskStack,对应前面AMS中的ActivityStack,这两者及其子结构会保持同步。从中我们可以发现,WMSAMS中的数据结构是有对应关系的,如AMS中的TaskRecordWMS中的TaskAMS中的ActivityRecordWMS中的AppWindowToken。另外WMSTaskStackmTasks需要和AMSActivityStackmTaskHistory顺序保持一致。


DisplayContent:表示一个显示设备上的内容,这个显示设备可以是外接显示屏,也可以是虚拟显示屏。其中mWindows是一个WindowState的有序(Z-ordered,底部最先)列表。mStackBoxes包含了若干个StackBox,其中一个为HomeStack,另一个是AppStackBox。所有的StackBox被组织成二叉树,StackBox是其中的节点,其中有三个重要成员变量,mFirstmSecond指向左和右子结点(也是StackBox),StackBox的成员mStack才是我们真正关心的东西-TaskStack。可以看到,为了要把TaskStack存成树的结构,需要一个容器,这个容器就是StackBoxDisplayContentStackBoxTaskStack的关系如下:


StackBox信息可以用am stack boxesdumpsys window displays命令查看:

$ adb shell am stackboxesWARNING: 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.5createStack returnednew stackId=4

然后再查看stack信息就变成了这样:

$ adb shell am stackboxesBox 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.CameraSecond 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

可见,在分屏情况下,这个结构以二叉树的形式分裂,形成这样的结构:


除了TaskStackDisplayContent中的成员mTaskHistory也包含了一个有序的Task列表。结合上面几个概念,可以得到下面的关系:


逻辑上,一个TaskTask)可以包含多个Activity(对应AppWindowToken),每个Activity可以包含多个窗口(对应WindowState),而每个窗口都是可能被放在任意一个显示屏上的(想象一个Windows操作系统中外接显示器的情况),因此就有了上面这个结构。从这个结构可以看出,WMS的主要任务之一就是维护各窗口的Z-order信息。Z轴可看作是屏幕法向量方向上的坐标轴,值越大的层意味着离用户越近,会把值小的窗口给盖住。一方面,WMS需要知道各窗口的遮挡关系来做layout和分配释放Surface,另一方面,这个Z-order信息会转化为窗口对应Surfacelayer属性输出到SF,指导SF的渲染。

那么,这个列表是怎么管理的呢?我们知道,每个窗口在WMS都有对应的WindowState,因此,本质上我们需要维护一个Z-order排序的WindowState列表。首先,TaskStack中包含了历史序的Task,每个Task又包含了Z-orderedAppWindowTokenAppWindowToken的成员windows又包含了一个Z-orderedWindowState列表。前面提到过,一个AppWindowToken对应AMS中的一个ActivityRecord,因此这个列表包含了这个Activity中的所有窗口,子窗口,开始窗口等。另一方面,DisplayContent中也有一个成员mWindows,指向一个Z-orderedWindowState列表(列队越前面的在越底部),它描述的是单个显示屏上的窗口集合。在添加窗口时会通过addAppWindowToListLocked()函数往这个窗口堆栈插入元素。由于插入过程要考虑子窗口,开始窗口等的偏移量,往DisplayContentmWindows插入元素时需考虑WindowToken中的windows列表。得到DisplayContent中的mWindows列表后,之后会调用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是唯一的继承类,这种结构看似可有可无,但如果以后厂商要加其它的策略,或更改已有策略,这种设计就能够提供良好的灵活性。

以上就是AMSWMS的大体框架。粗糙地说,AMS管理ActivityServiceProcess等信息,WMS管理应用和系统窗口。这两者既有联系又有很大不同,Activity一般有窗口,Service可以有也可以没有窗口,而窗口不一定非要对应Activity或者Service。只是很多时候一个Activity中就一个顶层视图,对应WMS一个窗口,所以会给人一一对应的错觉,但这两者其实没有直接关系。这就造就了AMSWMS两大模块,虽然其中数据结构有很多是保持同步的,但是设计上让它们分开,各司其职,异步工作。从窗口管理的流程来说,App负责视图树的管理和业务逻辑。AMS管理和调度所有App中的组件,通知WMS组件的状态信息。WMSAppSF申请和调整Surface,同时计算维护窗口的布局,z-order等信息,另外当显示状态发生变化时,WMS还要通知App作出调整。SFWMS拿到各窗口对应Surface的属性和layer信息,同时从App拿到渲染好的图形缓冲区,进行进一步的合并渲染,放入framebuffer,最后用户就能在屏幕上看到App的窗口了。


更多相关文章

  1. Android屏幕分辨率正确获取及PX,DPI,DP,SP等的对应关系
  2. IM-A820L限制GSM,WCDMA上网的原理(其他泛泰机型可参考)7.13
  3. Android——开发环境
  4. 2011年Android(安卓)Camera学习笔记之一
  5. Android(安卓)N 指纹框架
  6. Android中OpenMax的适配层
  7. Android获取屏幕大小
  8. Android(安卓)初识Retrofit
  9. 调出软键盘 挤掉标题栏咋办

随机推荐

  1. android按键定义
  2. Android(安卓)TextToSpeech语音播放文本
  3. Android通过JNI操作串口《一》
  4. android的service中在后台弹出提示框
  5. android build (可参考之建立android编译
  6. Android(安卓)NavigationView 侧滑菜单
  7. 更新android studio gradle 不成功解决方
  8. android 获取短信验证码倒计时
  9. Android获取默认浏览器信息
  10. build WebRTC for android