Android 4.4(KitKat)窗口管理子系统 - 体系框架

原文地址: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 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中需要看图片附件,然后会开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 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

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


除了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的窗口了。


展开阅读全文

Git 实用技巧

11-24
这几年越来越多的开发团队使用了Git,掌握Git的使用已经越来越重要,已经是一个开发者必备的一项技能;但很多人在刚开始学习Git的时候会遇到很多疑问,比如之前使用过SVN的开发者想不通Git提交代码为什么需要先commit然后再去push,而不是一条命令一次性搞定; 更多的开发者对Git已经入门,不过在遇到一些代码冲突、需要恢复Git代码时候就不知所措,这个时候哪些对 Git掌握得比较好的少数人,就像团队中的神一样,在队友遇到 Git 相关的问题的时候用各种流利的操作来帮助队友于水火。 我去年刚加入新团队,发现一些同事对Git的常规操作没太大问题,但对Git的理解还是比较生疏,比如说分支和分支之间的关联关系、合并代码时候的冲突解决、提交代码前未拉取新代码导致冲突问题的处理等,我在协助处理这些问题的时候也记录各种问题的解决办法,希望整理后通过教程帮助到更多对Git操作进阶的开发者。 本期教程学习方法分为“掌握基础——稳步进阶——熟悉协作”三个层次。从掌握基础的 Git的推送和拉取开始,以案例进行演示,分析每一个步骤的操作方式和原理,从理解Git 工具的操作到学会代码存储结构、演示不同场景下Git遇到问题的不同处理方案。循序渐进让同学们掌握Git工具在团队协作中的整体协作流程。 在教程中会通过大量案例进行分析,案例会模拟在工作中遇到的问题,从最基础的代码提交和拉取、代码冲突解决、代码仓库的数据维护、Git服务端搭建等。为了让同学们容易理解,对Git简单易懂,文章中详细记录了详细的操作步骤,提供大量演示截图和解析。在教程的最后部分,会从提升团队整体效率的角度对Git工具进行讲解,包括规范操作、Gitlab的搭建、钩子事件的应用等。 为了让同学们可以利用碎片化时间来灵活学习,在教程文章中大程度降低了上下文的依赖,让大家可以在工作之余进行学习与实战,并同时掌握里面涉及的Git不常见操作的相关知识,理解Git工具在工作遇到的问题解决思路和方法,相信一定会对大家的前端技能进阶大有帮助。
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值