目录
一、 Window、WindowManager、WMS
Window
:在Android视图体系中Window就是一个窗口的概念。Android中所有的视图都是依赖于Window显示的。
window 它是一个抽象类,具体实现类为 PhoneWindow ,它对 View 进行管理。Window是View的容器,View是Window的具体表现内容。
每个Activity都对应有一个Window
应用程序窗口、PopupWindow、输入法窗口、Toast、Dialog、系统错误窗口等都是比较常见的Window
WindowManager
:是一个接口类,继承自接口ViewManager
,从它的名称就知道它是用来管理 Window
的,它的实现类为 WindowManagerImpl
。对Window的管理,包括新增、更新和删除等。
横竖屏切换、配置的变化都会导致Window的更新
Dialog的dismiss()就是window的删除操作
WindowManagerService(WMS)
:窗口的最终管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由WMS进行管理。
三者的关系:
二、 WindowManager 的关联类
这个地方采用
桥接模式
WindowManager 其实是一个接口,它继承自 ViewManager , ViewManager 中定义了 3 个抽象方法,分别是用来添加、更新、删除 View 的,WindowManager 继承了父类接口的方法,说明也具备了父类的能力。
WindowManagerImpl是WindowManager的实现类,但是具体的功能都会委托给WindowManagerGlobal来实现。
三、 Window的类型
Window 的类型有很多种,比如应用程序窗口、系统错误窗口、输入法窗口、PopWindow、Toast、Dialog 等。总的来说 Window 分为三大类型,分别是 Application Window(应用程序窗口)
、Sub Window(子窗口)
、System Window (系统窗口)
,每个大类型中又包含了很多种类型,它们都定义在 WindowManager 的静态内部类 LayoutParams 中,接下来分别对这三大类型进行讲解。
-
Application Window
:Activity就是一个典型的应用程序窗口。
应用程序的窗口的 Type 值范围为1~99
,这个值的大小涉及窗口的层级。//WindowManager.java /** * 表示应用程序窗口类型初始值 */ public static final int FIRST_APPLICATION_WINDOW = 1; /** * 窗口的基础值,其它的窗口值要大于这个值 */ public static final int TYPE_BASE_APPLICATION = 1; /** * 普通的应用程序窗口 * */ public static final int TYPE_APPLICATION = 2; /** * 应用程序启动窗口的类型,用于系统在应用程序窗口启动前显示的窗口 */ public static final int TYPE_APPLICATION_STARTING = 3; public static final int TYPE_DRAWN_APPLICATION = 4; /** * 表示应用程序窗口类型的结束值值的范围是 1~99 */ public static final int LAST_APPLICATION_WINDOW = 99;
-
Sub Window
:子窗口,顾名思义,它不能独立存在,需要附着在其他窗口才可以,PopupWindow就属于子窗口。
子窗口的Type值范围为1000~1999
跟父窗口共用一个token
//WindowManager.java /** * 子类窗口初始化值 */ public static final int FIRST_SUB_WINDOW = 1000; public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1; public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3; public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4; public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; /** * 子类窗口类型结束值 */ public static final int LAST_SUB_WINDOW = 1999;
-
System Window
: 比如 Android 中的 Toast、输入法窗口、系统音量条窗口、系统错误窗口、顶部状态栏通知栏、Toast都是属于系统级别的 Window 。 系统窗口的类型值有接近 40 多个,这里只列举了部分,系统窗口的 Type 值范围为 2000 - 2999。/** * 系统类型窗口类型初始值 */ public static final int FIRST_SYSTEM_WINDOW = 2000; /** * 系统状态栏窗口 */ public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; /** * 搜索条窗口 */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; /** * 通话窗口 */ @Deprecated public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; /** * 系统 alert 窗口 */ @Deprecated public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; /** * 系统锁屏窗口 */ public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; /** * Toast 窗口 */ @Deprecated public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; ... /** * 系统窗口类型的结束值 */ public static final int LAST_SYSTEM_WINDOW = 2999;
Type值越大,Z轴越大,窗口越靠前
窗口的次序:
最上层的一定是系统窗口System Window,之后是子窗口Sub Window,最下面是应用程序窗口Application Window
不同类型的窗口的排列次序:
四、 Window的标志
Window 的标志也就是 Flag, 用于控制 Window 的现实,同样被定义在 WindowManager 的内部类 LayoutParams 中,一共有 20 多个,这里给出几个比较常用的,如下:
Window Flag | 说明 |
---|---|
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | 只要窗口可见,就允许在开启状态的屏幕上锁屏 |
FLAG_NOT_FOCUSABLE | 窗口不能获得输入焦点,设置该标志的同时,FLAG_NOT_TOUCH_MODAL 也会被设置 |
FLAG_NOT_TOUCHABLE | 窗口不接收任何触摸事件 |
FLAG_NOT_TOUCH_MODAL | 将该窗口区域外的触摸事件传递给其它的 Window,而自己只会处理窗口区域内的触摸事件 |
FLAG_KEEP_SCREEN_NO | 只要窗口可见,屏幕就会一直常亮 |
FLAG_LAYOUT_NO_LIMITS | 允许窗口超过屏幕之外 |
FLAG_FULISCREEN | 隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示 |
FLAG_SHOW_WHEN_LOCKED | 窗口可以在锁屏的窗口之上显示 |
FLAG_IGNORE_CHEEK_PRESSES | 当用户的脸贴近屏幕时(比如打电话),不会去响应事件 |
FLAG_TURN_SCREEN_NO | 窗口显示时将屏幕点亮 |
设置 Window 的 Flag 有 3 种方法:
-
通过 Window 的 addFlag 方法
Window mWindow = getWindow(); mWindow.addFlag(WindowManager.LayoutParams.FLAG_FULLSCREEN);
-
通过 Window 的 setFlags 方法
Window mWindow = getWindow(); mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN)
-
给 LayoutParams 设置 Flag, 并通过 WindowManager 的 addView 方法进行添加
WindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN; WindowManager mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE); Text mText = new Text(this); mWindowManager.addView(mTextView,mWindowLayoutParams);
五、 软件盘相关模式
窗口与窗口的叠加是十分常见的场景,但是如果其中的窗口是软件盘的窗口,可能就会出现一些问题,比如典型的用户登录页面,默认的情况弹出软件盘窗口可能遮挡输入框下方的按钮,这样用户体验非常糟糕。为了使得软键盘窗口能够按照期望来显示,WindowManager 的静态内部类 LayoutParams 中定义了软件盘相关模式,这里给出常用的几个:
SoftInputMode | 描述 |
---|---|
SOFT_INPUT_STATE_UNSPECIFIED | 没有设定状态,系统会选择一个合适的状态或依赖于主题的设置 |
SOFT_INPUT_STATE_UNCHANGED | 不会改变软键盘状态 |
SOFT_INPUT_STATE_ALWAYS_HIDDEN | 当窗口获取焦点时,软键盘总是被隐藏 |
SOFT_INPUT_ADJUST_RESIZE | 当软键盘弹出时,窗口会调整大小 |
SOFT_INPUT_ADJUST_PAN | 当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的 |
SOFT_INPUT_STATE_HIDDEN | 当用户进入该窗口时,软键盘默认隐蔽 |
从上面给出的 SoftInputMode,可以发现,它们与 AndroidManifest.xml 中 Activity 的属性 android:windowsoftInputMode 是对应的。因此,除了在 AndroidManifest.xml 中为 Activity 配置还可以通过代码动态配置,如下所示:
geWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
六、添加Window
Activity#attach()方法之内PhoneWindow
被创建
,并同时创建----WindowManagerImpl
负责维护
PhoneWindow内的内容。
在Activity#onCreate()
中调用setContentView()
方法,这个方法内部创建一个DecorView
实例作为PhoneWindow
的内容。
WindowManagerImpl
决定管理DecorView
,并创建一个ViewRootImpl实例,将ViewRootImpl
与View树
进行关联,这样ViewRootImpl就可以指挥
View树的具体工作。
七、 DecorView
activity 与 PhoneWindow 与 DecorView 关系
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图
在Activity的生命周期onCreate()
方法里面,是会设置好布局内容通过setContentView(布局id)
的方式,这里是通过xml解析器转化为一个View
,这个View会被添加到ContentView
中去,成为唯一的子View
。
八、 WindowManagerImpl、WindowManagerGlobal、ViewRootImpl
WindowManagerGlobal是一个单例类,一个进程只有一个实例
。它管理者所有Window的ViewRootImpl、DecorView、LayoutParams。
ViewRootImpl是View树的树根
,但它却又不是View
,实现了View与WindowManager之间的通信协议
,在WindowManagerGloble中的addView
中被建立,是顶层DecorView的ViewParent。
ViewRootImpl作用:
① View树的树根并管理View树
② 触发View的测量、布局和绘制
③ 输入响应的中转站
④ 负责与WMS进行进程间通信(通过binder)
一个Activity对应一个PhoneWindow,一个PhoneWindow里面包含一个WindowManagerImpl和一个DecorView,一个WindowManagerImpl对应一个ViewRootImpl,WindowManagerGlobal是一个全局的管理者。
-
WindowManagerImpl
主要功能:确定View属于哪个屏幕、哪个父窗口(确定窗口
) -
WindowManagerGlobal
主要功能:管理整个进程 所有的窗口信息,即主要包含view(DecorView)、root(ViewRootImpl)、wparams(WindowManager.LayoutParams)(管理信息
)每个进程都对应一个WindowManagrGlobal,也就说每个app进程都对应有自己的WindowManagrGlobal
-
ViewRootImpl
主要功能:WindowManagerGlobal实际操作者,操作自己的窗口(做大量的事情,真正的执行者
)ViewRootImpl存在多个,一个窗口对应一个ViewRootImpl
绘制流程:
更新Window:
ViewRootImpl.scheduleTraversals()打报告进行刷新,信号服务中心会收到VSYNC信号,收到信号之后会重新发送一个同步信号给到Choreographer编舞者,最后就会执行到ViewRootImpl.performTraverals()。即window刷新或view的刷新,最后都会调用到ViewRootImpl.performTraverals()方法中
。
UI刷新流程:
申请Vsync流程:
等到VSYNC到来后,会移除同步栅栏,并率先开始执行当前帧的处理,调用逻辑如下:
SurfaceFlinger
SurfaceFlinger是整个Android系统渲染的核心进程。所有应用的渲染逻辑最终都会来到SufaceFlinger中进行处理,最终会把处理后的图像数据交给CPU或者GPU进行绘制。
在每一个应用中都以Surface作为一个图元传递单元,向SurfaceFlinger这个服务端传递图元数据。
SurfaceFlinger整体流程:
SurfaceFlinger是以生产者以及消费者为核心设计思想,把每一个应用进程作为生产者生产图元保存到SurfaceFlinger的图元队列中,SurfaceFlinger则作为消费者依照一定的规则把生产者存放到SurfaceFlinger中的队列一一处理。