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

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

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

基于Binder的本地过程调用(LPC)让Android的模块间耦合度更低。结构更加清晰。每一个模块各司其职。并向其他模块提供接口。

进程和uid这些Linux中的机制对这些模块提供了天然的保护,使得系统更加鲁棒。模块之间常常使用C/S的结构,而Service本身也可能是使用其他Service的Client。举例来说,假设Service的实现叫XXXManagerService。那一般它对Client提供接口IXXXManager,然后Client要用Service的时候便会申请一个叫BpXXXManager的代理对象。它是远端BnXXXManager本地对象在Client端的代理。代理对象BpXXManager实现了IXXXManager的全部接口,仅仅只是里面的函数都是壳子,仅仅负责參数的准备,然后就调用远端对象去运行。

远端的的BnXXXManager对象及其继承类是真正做事的,BnXXXManager继承自IXXXManager.Stub抽象类,实现了IXXXManager接口。

Stub就如其名字一样,是BnXXXManager的继承类在BnXXXManager中的“钩子”。通过调用这些接口便能够调用到远端的Service功能了。概念上类似远程gdb调试。host机上的gdb和guest上的gdbserver相连以后,在host上敲命令会让gdbserver去运行。但感觉就像是在host本地运行一样。这儿的gdbserver就提供了类似于Stub的功能。

这样的远程调用模型的建立过程通常是分层次的。比方WindowManagerGlobal会与WMS进行连接,ViewRootImpl会与WMS中的Session进行连接。高层先与高层通信,同一时候帮助建立低层间的通信。然后低层与低层直接通信。

打个比方。张三是A部门的员工。他想要和B部门合作搞一个活动,他一般不会直接冲过去B部门挨个问的。所以他先和自己的主管李四说。我要和B部门合作,于是李四找到B部门的主管王五,说你出个人吧。于是王五和赵六说,你负责这事儿吧。并告诉了A部门主管李四。李四再告诉下属张三,赵六是B部门接口人,你以后直接和他联系吧。

于是张三和赵六以后就直接联系了。假设合作中有须要超越自己权限的操作。他们再向各自的主管申请。比方App与WMS的连接,首先会建立一个Session到WMS,之后就会通过IWindowSession接口与WMS中的Session直接通信。

还有比如WMS和SF先创建SurfaceSession,当中会创建SurfaceComposerClient,訪问SurfaceComposerClient时会在SF中创建Client与之相应,这个Client实现了ISurfaceComposerClient接口,之后SurfaceComposerClient会通过该接口与SF中的Client直接通信。

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

要注意的几点: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中的<application>标签。后者是配置文件里对组件的管理者,它和进程之间没有本质关系,通过android:process标签能够让同一个<application>下的组件跑在多个进程,也能够让多个<application>中的组件跑在同一个进程。所以假设是<application>定义的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进程,新App创建好后attach到AMS。

AMS再通知新App创建Activity等对应数据结构。

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

为了使系统中的策略更加灵活,easy替换,系统使用了一些设计模式将之从其他逻辑中解耦。

如IPolicy是一个工厂类接口。Policy为它的详细实现类。

它负责创建一系列策略相关的对象,如makeNewWindow()创建PhoneWindow等。同一时候PolicyManager还使用了Strategy模式将Policy包装起来,这为策略的替换提供了便利,也使执行时更换策略成为可能。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamluemh1b2p1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

尽管眼下为止貌似仅仅有一种针对“Phone”的策略,所以还没看到这样设计的优点。可是,一方面,或许将来在多合一的移动设备上,笔记本,平板什么的能够切换,那么Policy自然也须要动态切换。

Android里还有非常多把这样的Policy单独拎出来的样例,如WindowManagerPolicy类。还有一方面,Android作为一个框架,须要让各个厂商把Android用到自己的平台上更加easy适配。将来假设作为眼镜,车载。智能家电等等嵌入式设备的统一平台。假设将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()的流程例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamluemh1b2p1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

首先通过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<ActivityStack> mStacks = new ArrayList<ActivityStack>();

系统中的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

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamluemh1b2p1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

除了TaskStack。DisplayContent中的成员mTaskHistory也包括了一个有序的Task列表。

结合上面几个概念,能够得到以下的关系:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamluemh1b2p1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

逻辑上,一个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中也有一个成员mWindows,指向一个Z-ordered的WindowState列表(列队越前面的在越底部),它描写叙述的是单个显示屏上的窗体集合。在加入窗体时会通过addAppWindowToListLocked()函数往这个窗体堆栈插入元素。

因为插入过程要考虑子窗体,開始窗体等的偏移量,往DisplayContent的mWindows插入元素时需考虑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是唯一的继承类。这样的结构看似可有可无,但假设以后厂商要加其他的策略,或更改已有策略,这样的设计就行提供良好的灵活性。

以上就是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的窗体了。

版权声明:本文博主原创文章。博客,未经同意不得转载。

Android 4.4(KitKat)表格管理子系统 - 骨架的更多相关文章

  1. Android 4.4 KitKat 新特性

    New in Android 4.4 KitKat 本文是一个概览,关于KitKat,也即Android4.4的新东西,先是功能型的,之后是设计上的. 很多特性本文并没有提到,很多提到的特性也只是简短 ...

  2. Android 4.4 KitKat, the browser and the Chrome WebView

    Having V8 as the JavaScript engine for the new web view, the JavaScript performance if much better, ...

  3. 让你的短信应用迎接Android 4.4(KitKat)

    原文地址:Getting Your SMS Apps Ready for KitKat 发送和接收短信是手机最基本的功能,很多的开发者也开发了很多成功的应用来增强Android这一方面的体验.你们当中 ...

  4. Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 10

    今天编译一个project,我设置为api 14,可是编译报错: Using 1.7 requires compiling with Android 4.4 (KitKat); currently u ...

  5. Android 4.4(KitKat)中VSync信号的虚拟化

    原文地址:http://blog.csdn.net/jinzhuojun/article/details/17293325 Android 4.1(Jelly Bean)引入了Vsync(Vertic ...

  6. Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  7. GetPathFromUri4kitkat【Android 4.4 kitkat以上及以下根据uri获取路径的方法】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 在Android4.4之前和之后,通过Intent调用文件管理器选择文件,获取的文件uri地址形式是不同的. Android6.0 ...

  8. Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象.与Google原生AOSP有些许差异.请读者知悉. ...

  9. 解决Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 4

    有时候我们可能需要将项目的版本降低,比如4.4降低到2.2这样的,可能会遇到类似于这样的错误 Using 1.7 requires compiling with Android 4.4 (KitKat ...

随机推荐

  1. opencv环境的搭建,并打开一个本地PC摄像头。

    1.opencv环境结构 推荐连结 http://www.cnblogs.com/Anykong/archive/2011/04/06/Anykong_OpenCV1.html 2.以下是基本測试,和 ...

  2. effective c++ 条款12 copy all parts of an object

    这经常发生在更改代码的时候,当有自己的copy 赋值函数或者copy 构造函数时,编译器就不会维护这两个函数.导致发生遗忘. 可能出现的场景 class Customer { private: std ...

  3. ueditor问题解决

    ueditor图片无法上传? 解决: imageUp.ashx 去掉这一行 <%@ Assembly Src="Uploader.cs" %> 参考: http://w ...

  4. 【Linux探索之旅】第二部分第三课:文件和目录,组织不会亏待你

    内容简介 1.第二部分第三课:文件和目录,组织不会亏待你 2.第二部分第四课预告:文件操纵,鼓掌之中 文件和目录,组织不会亏待你 上一次课我们讲了命令行,这将成为伴随我们接下来整个Linux课程的一个 ...

  5. 2.大约QT数据库操作,简单的数据库连接操作,增删改查数据库,QSqlTableModel和QTableView,事务性操作,大约QItemDelegate 代理

     Linux下的qt安装,命令时:sudoapt-get install qt-sdk 安装mysql数据库,安装方法參考博客:http://blog.csdn.net/tototuzuoquan ...

  6. [LeetCode235]Lowest Common Ancestor of a Binary Search Tree

    题目: Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in th ...

  7. java提高篇(三)-----理解java的三大特性之多态

    面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承 ...

  8. 什么是PV,UV。

    PV浏览(Page View).该网页访问量,每次页面打开PV统计+1,也刷新. IP接入号码指独立IP接入号码,计算基于独立IP在计算的时间段来计算访问我们的网站1二级IP接入号码. 是否这个计算在 ...

  9. Google API快速生成QR二维码

    Google API快速生成QR二维码 现在来说生成二维码最简单的方法是使用Google Chart API来实现,再次膜拜Google大神- Google Chart API是一套可以让你在线生成报 ...

  10. 接近带给你AngularJS - 经验说明示例

    接近带给你AngularJS列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自己定义指令 ------------ ...