窗口层级相关
WindowContainer
1 | //frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java |
给可以直接持有窗口的自己或它的孩子定义了一些公共的方法和属性,RootWindowContainer、DisplayContent、DisplayArea、DisplayArea.Tokens、TaskDisplayArea、Task、ActivityRecord、WindowToken、WindowState都是直接或间接的继承该类。
主要的成员变量就是mParent和mChildren,一个代表父节点一个代表子节点,而且子节点的list顺序代表就是z轴的层级显示顺序,list尾巴在比list的头的z轴层级要高。
RootWindowContainer:根窗口容器,树的根是它。通过它遍历寻找,可以找到窗口树上的窗口。它的孩子是DisplayContent。
DisplayContent:该类是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个DisplayContent对象。
DisplayArea:该类是对应着显示屏幕下面的,代表一组窗口合集,具有多个子类,如Tokens,TaskDisplayArea等
TaskDisplayArea:它为DisplayContent的孩子,对应着窗口层次的第2层。第2层作为应用层,看它的定义:int APPLICATION_LAYER = 2,应用层的窗口是处于第2层。TaskDisplayArea的孩子是Task类,其实它的孩子类型也可以是TaskDisplayArea。而Task的孩子则可以是ActivityRecord,也可以是Task。
Tokens:代表专门包含WindowTokens的容器,它的孩子是WindowToken,而WindowToken的孩子则为WindowState对象。WindowState是对应着一个窗口的。
ImeContainer:它是输入法窗口的容器,它的孩子是WindowToken类型。WindowToken的孩子为WindowState类型,而WindowState类型则对应着输入法窗口。
Task:任务,它的孩子可以是Task,也可以是ActivityRecord类型。
ActivityRecord:是对应着应用进程中的Activity的。ActivityRecord是继承WindowToken的,它的孩子类型为WindowState。
WindowState:WindowState是对应着一个窗口的。
用dumpsys命令来看看层级结构相关的输出
1 | adb shell dumpsys activity containers |
对应的输出结构树如下
每个显示屏幕的窗口层级分为37层,0-36层。每层可以放置多个窗口,上层窗口覆盖下面的,这个树中每个点一般这样格式:
名字:层级开始数 :层级结束数
0-36个图层每个图层都有对应节点进行占领,有的一个节点占领一层,如Leaf:36:36 ,有的一个节点可能占领多层如:Leaf:3:12
常用窗口挂载层级:
- Wallpaper处于0-1层
- Activity处于DefaultTaskDisplayArea第2层 后续挂载节点Task->ActivityRecord ->WindowState
- InputMethod处于13-14层
- StatusBar处于15层
- NotificationShade处于17层
- NavigationBar0处于24-25层
结构树构建的源码
DisplayContent中启动层级树的构建
1 | /** |
接下来调用DisplayAreaPolicy中内部类DefaultProvider的instantiate方法
1 | //frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicy.java |
先看下configureTrustedHierarchyBuilder
1 | private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy, |
设置的这些Feature名字,Feature代表的是DisplayArea的一个特征,可以根据Feature来对不同的DisplayArea进行划分。Feature类中成员变量如下:
- mName:这个Feature的名字,如上面的“WindowedMagnification”,“HideDisplayCutout”之类的,后续DisplayArea层级结构建立起来后,每个DisplayArea的名字用的就是当前DisplayArea对应的那个Feature的名字。
- mId:Feature的ID,如上面的FEATURE_WINDOWED_MAGNIFICATION和FEATURE_HIDE_DISPLAY_CUTOUT,虽说是Feature的ID,因为Feature又是DisplayArea的特征
- mWindowLayers:代表了这个DisplayArea可以包含哪些层级对应的窗口
其中一个Feature:
1 | rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification", |
这里调用了getWindowLayerFromTypeLw来实现窗口类型到层级数的转化:
1 | default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow, |
上面是应用常用的窗口类型,如TYPE_WALLPAPER,TYPE_NAVIGATION_BAR等,其实他们都是有固定的一个层级的。即windowType的值并不是真正层级数目,都是需要通过这个方法进行转化才是真正层级数。
再回到addFeature部分,通过以上的层级获取及相关upTo方法后我们可以得出各个Feature的一个层级情况
Feature名字 | 层级情况 |
---|---|
WindowedMagnification | 0-31 |
HideDisplayCutout | 0-14 16 18-23 26-35 |
OneHanded | 0-23 26-32 34-35 |
FullscreenMagnification | 0-12 15-23 26-27 29-31 33-35 |
ImePlaceholder | 13-14 |
每个Feature对应层级已经清楚了,再接下来就要进入正式的树构建
1 | //frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java |
大概步骤如下:
- 创建PendingArea作为root部分
- 根据之前的几个Feature的配置进行树图构造
- 根据窗口层级0~36层,每一层进行遍历,挂载一个新的叶子TOKENS节点,规则和前面Feature一样,如果同一个父亲则不需要新生成,针对TYPE_INPUT_METHOD和APPLICATION_LAYER需要进行特殊处理
- 把PendingArea生成DisplayArea
添加层级树
1、Task、ActivityRecord的添加
2、普通WindowToken的添加
Task的添加
DefaultTaskDisplayArea -> Task ->ActivityRecord
在WindowContainer添加堆栈打印
1 | protected void addChild(E child, Comparator<E> comparator) { |
打开电话应用堆栈日志
1 | DefaultTaskDisplayArea@171686050 addChild index c=Task{56a0fdb #208 type=standard ?? U=0 visible=false visibleRequested=false mode=undefined translucent=true sz=0} |
普通WindowToken的添加
Leaf容器加入StatusBar的WindowToken对应堆栈
1 | Leaf:15:15@65133355 addChild Comparator child = WindowToken{9dd676f type=2000 android.os.BinderProxy@ce5e149} |
WindowToken加入对应的StatusBar 的WindowState
1 | WindowToken{9dd676f type=2000 android.os.BinderProxy@ce5e149} addChild Comparator child = Window{b94d567 u0 StatusBar} |
添加Window操作
1 | public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, |
总结addWindow主要干了以下几件事:
- 创建WindowToken并挂载到对应的节点层级
- WindowState初始化和相关变量设置校验
- 开启与InputDispatcher通信的Socketpair
- WindowState挂载到WindowToken
relayout操作
1 | public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, |
主要就有2个最关键步骤:
1、创建对应Window的SurfaceControl
2、计算出对应的window区域等,把inset和config传递回去
窗口动画
Animator 对SurfaceA进行动画控制Matrix(放大缩小位移)alpha圆角等
- LocalAnimation:systemserver自己在WindowAnimator类进行动画播放控制
- RemoteAnimation:systemserver负责提供对应的SurfaceControl给远端进程让远端进行动画播放控制
窗口动画执行执行过程中会在窗口层级树中窗口节点和父节点中间插入一个leash节点对窗口进行控制,播放结束移除节点
Activity添加显示过程
Activity#attach()方法之内PhoneWindow被创建,并同时创建一WindowManagerImpl负责维护PhoneWindow内的内容。
在Activity#onCreate()中调用setContentView()方法,这个方法内部创建一个DecorView实例作为PhoneWindow的实体内容。
WindowManagerImpl决定管理DecorView,并创建一个ViewRootImpl实例,将ViewRootImpl与View树进行关联,这样ViewRootImpl就可以指挥View树的具体工作。
Activity resume时调用WindowManagerImpl.addView然后一步步调用WMS中的Window添加。
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。
在Activity的生命周期onCreate方法里面,是会设置好布局内容通过setContentView(布局id)的方式,这里是通过xml解析器转化为一个View,这个View会被添加到ContentView中去,成为唯一的子View。
WindowManagerGlobal是一个单例类,一个进程只有一个实例。它管理者所有Window的ViewRootImpl、DecorView、LayoutParams。
ViewRootImpl是View树的树根,但它却又不是View,实现了View与WindowManager之间的通信协议,在WindowManagerGloble中的addView中被建立,是顶层DecorView的ViewParent
View树的树根并管理View树
触发View的测量、布局和绘制
输入响应的中转站
负责与WMS进行进程间通信。
WMS是窗口的管理者,它负责窗口的启动、添加和删除,窗口动画。另外窗口的大小和层级也是由WMS进行管理的。WMS中对应的窗口在SurfaceFlinger中都有对应的layer对应,后续有机会看SurfaceFlinger代码再具体分析,还有窗口相关的事件分发处理。