Android底层分析(二)——WindowManager

在了解WindowManagerService之前,先来认识一下它的其他伙伴们

1. WindowManager概述

android\view\WindowManager.java

image-20220723103936510

WindowManager本身是一个接口,并且继承自ViewManager

ViewManager的内容比较简单,就是三件套

android\view\ViewManager.java

image-20220723104751374

这三个方法分别对应了View的添加、更新和删除

并且这些方法都传入View作为参数,因此Window正是借助于View的形式得以展示

既然WindowManager继承了ViewManager接口,自然也就继承了这些方法,可用于对于View进行操作

android\view\WindowManager.java

image-20220723130309361

除此以外,基于Window的特性,又加入了getDefaultDisplay()removeViewImmediate()这两个方法

第一个getDefaultDisplay()返回的是WindowManager将创建WindowDisplay对象

而第二个removeViewImmediate()则是removeView()的一种变体,能够在返回前立即调用给定View的层级的View#onDetachedFromWindow()方法,这不适用于正常的应用销毁流程

2. Window概述

Window是一个抽象类,正如注释所写,它唯一的实现类PhoneWindow

Window主要的职责是作为View容器,以及定义标准UI的一些基础规范

android\view\Window.java

image-20220723132617980

简要阐述一下Window在何时进行实例化

这里直接来到Activity的代码中,在启动Activity时,会调用到attach(),此时,Window对象进行实例化

android\app\Activity.java

image-20220723133536559

同样在attach()中,下面又调用了setWindowManager()方法,该方法定义在Window

image-20220723133923017

WindowManager为空时,会调用ContextgetSystemService()方法进行获取,这里传入服务名称Context.WINDOW_SERVICE

android\view\Window.java

image-20220723134334675

该服务的名称就是window

android\content\Context.java

image-20220723141029044

ContextImpl覆写了ContextgetSystemService()方法,这里涉及到装饰器模式

android\app\ContextImpl.java

image-20220723143718291

进而,调用SystemServiceRegistrygetSystemService()SYSTEM_SERVICE_FETCHERS是一个ArrayMap结构,从中获取对应服务的ServiceFetcher

android\app\SystemServiceRegistry.java

image-20220723145446838

ServiceFetcher作为SystemServiceRegistry的内部接口,通过getService()拿取服务,注意这里有缓存创建CachedServiceFetcher实现了ServiceFetcher接口

image-20220723150053219

继续看下面的代码,如果有对应的服务缓存,直接返回;如果是第一次,会去调用createService()方法

image-20220723150549643

SystemServiceRegistry类的static{}中注册了对应服务,createService()的抽象方法被覆写,最终作为服务返回的是WindowManagerImpl对象

image-20220723151143246

那么,取出的WindowManagerImpl被转为WindowManager,回到Window.java,下面又转回来

android\view\Window.java

image-20220723152230611

调用createLocalWindowManager()实质上是依据传入的Window对象new了一个WindowManagerImpl对象

android\view\WindowManagerImpl.java

image-20220723152736906

此时,新的WindowManagerImpl对象便持有了原来Window的引用,那么通过Window#getWindowManager()可以获得操作Window的权力,因为这正好获取了Window中的mWindowManager成员,实际的功能还是依赖于WindowManagerImpl

image-20220723153322958

但是,在WindowManagerImpl会发现,其实WindowManagerImpl也是把任务交给了mGlobal来做

image-20220723153940042

mGlobalWindowManagerGlobal单例对象WindowManagerGlobal进程唯一,但是WindowManagerImpl的实例一个进程有可能有多个),这里用到了桥接模式

image-20220723154339448

3. 关系图

简要描述下操作Window前都做了些什么

image-20220723161611799

其次,看下一些主要类的层次结构关系,仅标注一些主要的内容

image-20220723163459710

4. 属性

Window的属性有很多种,其中最常用到的主要有三种:

  • Type(类型)
  • Flag(标志位)
  • SoftInputMode(软键盘模式)

4.1. Window的类型以及显示的次序

Window的主要类型有以下三种:

  • 常规的应用程序窗口
  • 子窗口
  • 系统专用窗口

4.1.1. 常规的应用程序窗口

比如说Activity就属于这个范畴

对应的type数值为1~99

image-20220723192143302

4.1.2. 子窗口

这种窗口类型无法独立存在需要依附在其他窗口之上

PopWindow就属于该范畴

对应的type数值为1000~1999

image-20220723192248088

4.1.3. 系统专用窗口

Toast、软键盘、系统错误弹窗等窗口属于该范畴

系统专用窗口的类型还是比较多的,其对应type为2000~2999

image-20220723194545782

4.1.4. 窗口显示次序

进程向WMS申请窗口进行显示,WMS为窗口确定显示的次序

正如CSS里面的z-index属性一样,在垂直于屏幕的方向上,Android也提供Z-Order的概念,用于表示页面显示层级

前面提到的Window对应的type正好就是Z-Order进行排序的依据

一般情况下type的值越大,那么窗口显示的层级越靠上,也就越接近用户

4.2. 标志位

flag的主要用途是赋予窗口某些特性,比如不可接收触摸事件、触发亮屏等

image-20220723195820899

4.3. 软键盘模式

主要用于用户输入时,页面与软键盘弹窗叠加时的显示模式,这个很大程度上会影响用户的交互体验,现实场景中其实很重要

image-20220723200941432

5. 操作

对于Window的操作可以分为两个部分:

  • WindowManager的处理
  • WMS的处理

5.1. 系统窗口的添加(状态栏为例)

StatusBar#start()中,createAndAddWindows()所做的就是构建StatusBar的视图,并且将其添加到WindowManager

com\android\systemui\statusbar\phone\StatusBar.java

image-20220723224625407

其中,makeStatusBarView()所做的就是构建视图

image-20220724081218375

然后,接下来StatusBarWindowController#attach()将对应的视图添加到WindowManager上,同时也可以看到类型参数TYPE_STATUS_BAR

com\android\systemui\statusbar\phone\StatusBarWindowController.java

image-20220724081928982

调用addView()进行添加,这还是调用了WindowManagerImpl的方法,传入对应View参数

android\view\WindowManagerImpl.java

image-20220724082223760

作为子窗口时,adjustLayoutParamsForSubWindow()调整窗口的大小,适配当前的父窗口

android\view\WindowManagerGlobal.java

image-20220724082541336

这里先看看类开头定义的3个参数,mViewsmRootsmParams,都是ArrayList

image-20220724083304660

随后接着看addView(),实例化ViewRootImpl对象,为View设置窗口参数,然后三个列表对应保存数据,然后将设置完的View塞给ViewRootImpl对象

image-20220724082831155

ViewRootImpl承担了很多职责,比如View树管理,测量、布局、绘制,与WMS通信等

这里调用addToDisplayAsUser()mWindowSession是一个Binder对象,是IWindowSession类型,用以进行进程间通信IWindowSession客户端代理,与之对应的服务端实现为SessionSessionaddToDisplayAsUser()方法运行在WMS所在进程(system_server)

android\view\ViewRootImpl.java

image-20220724084224999

接受到通信,SessionaddToDisplayAsUser()被调用,Session被作为参数,WMS调用addWindow(),将其传入,剩下的工作都交由WMS处理了

com\android\server\wm\Session.java

image-20220724090051102

image-20220724092131849

5.2. Activity添加过程

performResumeActivity()最终会调用ActivityonResume(),然后getWindowManager(),这是之前的,返回的其实是WindowManagerImpl

android\app\ActivityThread.java

image-20220724092659689

调用addView(),之后和其他的Window添加过程就很类似了,这里传入的是DecorView

image-20220724092932953

5.3. Window更新

更新过程的起点从ViewManagerupdateViewLayout()开始,实际上调用了WindowManagerImplupdateViewLayout()

android\view\WindowManagerImpl.java

image-20220724093834650

然后,也是由WindowManagerGlobal管事,同样是操作三个列表,调用ViewRootImplsetLayoutParams()

android\view\WindowManagerGlobal.java

image-20220724094057194

ViewRootImplsetLayoutParams()最后调用了scheduleTraversals()

android\view\ViewRootImpl.java

image-20220724094353430

如果使用过SysTrace这类工具应该很熟悉这个名字,Choreographer,这里与UI绘制开始关联上了,其主要任务是接受显示系统的VSync信号,在下一帧渲染时进行一些同步操作

postCallback()发送了一个线程任务TraversalRunnable

image-20220724094618665

字面上看,叫“遍历线程”,它的线程任务doTraversal()

image-20220724095124212

所做的主要事情都在performTraversals()

image-20220724095340908

performTraversals()中的代码量非常大,都是与View绘制相关的,所谓的遍历其实就是遍历视图树

首先,relayoutWindow()实际会去调用mWindowSession.relayout(),这又是一个Binder通信,和之前一样,又是WMS去处理

image-20220724095639510

随后,是自定义View需要了解的三连,也就是View的工作流程,这几个方法分别对应了ViewonMeasure()onLayout()onDraW()

image-20220724095821478

image-20220724095903575

image-20220724095951390

一方面通知WMS去更新,另一方面进行View的绘制,完成了Window的更新