Android窗口添加流程

1、Activity的窗口结构

窗口结构图

说明

  • 每一个Activity都包含一个Window对象,Window对象通常由PhoneWindow实现

  • PhoneWindow:将Decoriew设置为整个应用窗口的根View。是Window的实现类。它是Android中的最基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。

  • DecorView:DecorView 是继承了FrameLayout(帧布局),DecorView 中含有两个子 View 分别是:ActionBarView 和 ContentView,我们一般是把布局设置在ContentView。在 Activity 的 onCreate() 方法中有 setContentView() 方法,这个方法设置布局实际上就是把布局放在ContentView 中;还需要知道:DecorView是视图树的最顶层,作用是承载布局。

  • 需要说明的是,Window是一个抽象的概念,我们在屏幕上看到的那些视图都是view,Window只是用来承载这些view的。

2、WindowManager体系

2.1、WindowWindowManagerWMS

Window上面已经说过了,它是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理。

WindowManager是一个接口类,继承自接口ViewManager,从名称就知道它是用来管理Window的,它的实现类为WindowManagerImpl。如果我们想要对Window进行添加和删除就可以使用WindowManager。

WMS:WindowManager最终的工作都是由WMS来处理的,WindowManager和WMS通过Binder来进行跨进程通信,WMS作为系统服务有很多API是不会暴露给WindowManager的,它的主要功能包括Window管理和输入系统就。

Window包含了View并对View进行管理,Window是一个抽象概念,并不是真实存在,Window的实体其实也是View。WindowManager用来管理Window,而WindowManager所提供的功能最终会由WMS来进行处理

 

2.2WindowManager如何对Window进行管理的

 

先来一张图吧

ViewManager

ViewManager是一个接口,里面定义了addView、removeView、updateViewLayout三个接口,可以看到这个三个接口传入的参数都是View类型,也就间接说明WindowManager其实是以View的形式来管理Window的,还有ViewGroup也是实现的这个接口。

WindowManager

可以看到WindowManager是一个接口,而且它继承与ViewManager。WindowManager字面理解就是窗口管理器,每一个窗口管理器都与一个的窗口显示绑定。获取实例可以通过
Context.getSystemService(Context.WINDOW_SERVICE)获取。既然继承了ViewManager,那么它也就可以进行添加删除View的操作了,不过它的操作放在它的实现类WindowManagerImpl里面。成员变量:

  • BadTokenException:则是addView时它的LayoutParams无效则会被抛出,或是添加第二个View的时候没有移除第一个View则会被抛出

  • InvalidDisplayException:如果一个窗口是在一个二级的显示上而指定的显示找不到则会被抛出

  • getDefaultDisplay:返回当前WindowManager管理的显示Display

  • removeViewImmediate:表示从窗口上移除View,一般是当View调用了onDetachedFromWindow也就是从Window上分开后,把它移除。

  • LayoutParams:静态内部类。显然是Window的布局参数,里面定义了一系列的窗口属性。

WindowManagerImpl

WindowManagerImpl是WindowManager的实现类,可以看到WindowManagerImpl里面有一个成员变量WindowManagerGlobal,而真正的实现则是在WindowManagerGlobal了,类似代理,只不过WindowManagerGlobal是个没有实现WindowManager的类的,自己定义了一套实现。WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实例。

WindowManagerGlobal

3、从应用启动到WindowManager的创建

大概了解了上述类的分类和各自的作用,接下来通过一个具体的场景来看下这些类的对象都是如何创建的,以及如何关联起来的,我们就以创建启动一个Activity为例。调用startActivity后就会拉起并显示这个Activity,具体的启动过程这里就不讲了,直接看ActivityThread.handleLaunchActivity()

frameworks/base/core/java/android/app/ActivityThread.java

  frameworks/base/core/java/android/view/WindowManagerGlobal.java

可以看到 WindowManagerGlobal.initialize()则通过WindowManagerGlobal创建了WindowManagerServer,接下来调用了performLaunchActivity -→activity.attach()

frameworks/base/core/java/android/app/Activity.java

注释1处创建了PhoneWindow,在注释2处调用了PhoneWindow的setWindowManager方法,这个方法的具体的实现在PhoneWindow的父类Window中。

frameworks/base/core/java/android/view/Window.java

这里其实就是初始化了PhoneWindow所持有的mWindowManager,得到的是WindowManagerImpl实例,最后又调用了WindowManagerImpl的createLocalWindowManager方法:

frameworks/base/core/java/android/view/WindowManagerImpl.java

 createLocalWindowManager方法同样也是创建WindowManagerImpl,不同的是这次创建WindowManagerImpl时将创建它的Window作为参数传了进来,这样WindowManagerImpl就持有了Window的引用,就可以对Window进行操作,比如在Window中添加View,来查看WindowManagerImpl的addView方法:

调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是Window。关于WindowManagerGlobal最终是如何调用到WMS的,后文在讲。

总结一下:

1、Activity启动的时候后创建一个PhoneWindow,PhoneWindow继承自Window;

2、Window通过setWindowManager方法创建WindowManager的实现类WindowManagerImpl,并且互相绑定,WindowManagerImpl持有Window,就可以对Window进行操作了;

3、WindowManagerImpl是WindowManager接口的实现类,但是具体的功能都会委托给WindowManagerGlobal来实现WindowManagerGlobal是有一个单例,在创建WindowManagerImpl时初始化。

 

4、从setContentView到DecorView的add过程

4.1、xml加载到DecorView

activity.attach()执行完后,接着就会回调Activity.onCreate(),Activity中要加载布局文件,就会调用setContentView()

frameworks/base/core/java/android/app/Activity.java

getWindow()返回的是mWindow,就上attach()方法中创建的PhoneWindow,继续看PhoneWindow中的setContentView()方法:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

注释1处会创建DecorView视图,注释2就是解析我们自己写的xml布局,并加载到mContentPatent。mContentPatent是啥?先看看它赋值的地方吧,在注释1的installDecor()中:

注释1处实例化mDecor,其实就是new DecorView(),注释2处调用generateLayout()获取mContentPatent,这个方法太长了,就不贴出来了,这个方法返回值是contentParent,直接看是在哪初始化的:

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

在来看看这个 ID_ANDROID_CONTENT是啥

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

看到这个id是不是很熟悉,就是第一章节窗口结构图中的ContentView所对应的id,我们的xml布局就是加载在这个View中。

到现在为止将我们要显示的布局添加到DecorView上了,那么DecorView又如何添加到Window上?

4.2、将DecorView添加到Window上

onCreate()走完了,之后就要执行onResume(),回到ActivityThread中的handleResumeActivity()方法

frameworks/base/core/java/android/app/ActivityThread.java

注释1处获取当前Activity的DecorView并设置为可见状态,注释2处获取WindowManager,其实是WindowManagerImpl的实例,注释3处调用了addView,这里就是将DecorView添加到Window,其实是调用了WindowManagerGlobal.addView()

frameworks/base/core/java/android/view/WindowManagerGlobal.java

 

注释1处创建了ViewRootImp并赋值给root,紧接着在注释2处将root存入到ArrayList<ViewRootImpl>类型的mRoots中,除了mRoots,mViews和mParams也是ArrayList类型的,分别用于存储窗口的view对象和WindowManager.LayoutParams类型的wparams对象。注释3处调用了ViewRootImpl的setView方法。

ViewRootImpl是一个很核心的类,身负了很多职责:

  • View树的根并管理View树

  • 触发View的测量、布局和绘制

  • 输入事件的中转站

  • 管理Surface

  • 负责与WMS进行进程间通信

frameworks/base/core/java/android/view/ViewRootImpl.java

setView方法中有很多逻辑,这里只截取了一小部分,注释1处requestLayout()是一个很重要的方法,View的测量、布局、绘制都是在这里完成的,总之会完成View显示前的一切准备工作,注释2处很明显是通过AIDL远程调用,此前包含ViewRootImpl在内的代码逻辑都是运行在本地进程的,从这里开始就进入服务端了。

先看下mWindowSession是啥?mWindowSession是在ViewRootImpl的构造方法中调用WindowManagerGlobal.getWindowSession()初始化的。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

注释1处获取WMS本地代理,注释2处通过AIDL调用到WMS.openSession()

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

这里Session就是服务端的Binder实例,并且持有WMS对象,运行在服务端;mWindowSession是IWindowSession类型的,是客户端的代理,mWindowSession.addToDisplay()最终是调到了Session. addToDisplay()

frameworks/base/services/core/java/com/android/server/wm/Session.java

这里的mService就是WMS,终于是来到了WMS.addView(),并将自身也就是Session,作为参数传了进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session,这样剩下的工作就交给WMS来处理。

总结一下:

1、ActivityonCreate()调用到了setContentView()后,会把视图添加到DecorView,但是此时View并没有开始绘制(没有触发view.measure,layout,draw)

2、DecorView真正的绘制显示是在activity.handleResumeActivity方法中DecorView被添加到WindowManager,然后调用ViewRootImpl.requestLayout()执行测量和绘制,并且绘制好视图后,交给WMS处理。

3、APP与WMS是通过Session这个Binder对象进行IPC通讯的,每个进程都有一个对应的Session,WMS保存所有的Session。

运行下面代码会报错么?

5、WMS的addView流程

addView方法比较长,处理的事物也很多,分开说

注释1处是权限检查,主要是对窗口type和调用者是否有添加相应权限进行检查;

注释2处通过displayId来获得窗口要添加到哪个DisplayContent上,DisplayContent用来描述一块屏幕;

注释3和4判断如果当前是子窗口就去获取它的父窗口,获取方式是根据attrs.token作为key值从mWindowMap集合中去查找,mWindowMap是WMS的成员变量,所有的窗口都保存在这个集合中。如果父窗口为null或者type值不对,会直接返回错误的状态。

注释5、6、7、8根据不同的场景创建WindowToken,WindowToken可以理解为窗口令牌,当应用程序想要向WMS申请新创建一个窗口,则需要向WMS出示有效的WindowToken,每个Activity都对应一个AppWindowToken。WindowToken会将相同组件的窗口(WindowState)集合在一起,方便管理。

注释9处创建了一个WindowState,客户端描述一个窗口是Window(PhoneWindow),对应WMS服务端描述一个窗口就是WindowState,它存有窗口的所有的状态信息,可以发现构造方法传入的参数中包含了WMS、Session、WindowToken、父类的WindowState、LayoutParams等信息。

注释10处是判断客户端进程是否是存活的,注释11根据窗口的type对窗口的LayoutParams的一些成员变量进行修改,注释12处设置窗口是否可以显示在当前Uid之外的屏幕上,注释13用于准备将窗口添加到系统中。

注释15把WindowState添加到mWindowMap集合中,注释14处attach()调到了Session.windowAddedLocked()

这里创建 了SurfaceSession 对象,SurfaceSession的构造方法中通过JNI调用nativeCreate(),这个native方法中创建 了一个SurfaceComposerClient 对象, 作为跟 SurfaceFlinger 通信的代理对象,最终是通过 SurfaceFlinger将视图合成显示到屏幕上。

总结一下,addWindow()方法展示了三个重要的概念,分别是WindowTokenWindowState以及DisplayContentWMS中添加窗口必须指明其所属的WindowToken窗口在WMS中通过一个WindowState实例进行管理和保管。同时必须在窗口中指明其所属的DisplayContent,以便确定窗口将被显示到哪一个屏幕上。

最后来一张全部过程的流程图:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值