Window
https://www.cnblogs.com/huan89/p/14111360.html
从来都没什么Window,有的只是一个个View树,每个窗口(activity/dialog/popupWindow)都是一个view树(代码中都是叫addView,直到WMS中才叫addWindow),在需要显示时被添加进WMS中,最后通过surfalceFlinger合成后渲染到屏幕中。
每个窗口都对应一个Token,一个应用只对应一个session。
从Window视角看ActivityStart
Activity
简述:首先回顾一下,activity是怎么显示出来的:
- 为activity创建PhoneWindow和WindowManager(WindowManagerImpl)对象
在handleLaunchActivity()被回调的时候,调用WindowManagerGlobal.initialize();
初始化WindoWindowManagerGlobal,之后 Application app = r.packageInfo.makeApplication(false, mInstrumentation);
创建Application(如果还没有创建过Application),然后调用activity.attach()
,这里面 mWindow = new PhoneWindow(this, window, activityConfigCallback);
初始化PhoneWindow并给它设置WindowManager,
1 | mWindow.setWindowManager( |
Window.setWindowManager()中通过WMS创建了WindowManagerImpl(其实就是个一百多行代码的壳)。
从这里可以看到是利用系统服务的windowManager来创建新的windowManagerImpl,因而这个应用所有的WindowManagerImpl都是同个内核windowManager,而创建出来的仅仅是包了个壳。
- setContentView实际上掉的是getWindow()(也就是上面的PhoneWindow)的setContentView(),其中调用PhoneWindow.installDecor(),
- 首先看decorView创建了没有,没有的话创建DecorView
- 把布局加载到DecorView中(LayoutInflater加载预设模板布局,见下)
DecorView是在PhoneWindow中预设好的一个布局,这个布局长这样:
他是一个垂直排列的布局,上面是ActionBar,下面是ContentView,他是一个FrameLayout。我们的Activity布局就加载到ContentView里进行显示。所以Decorview是Activity布局最顶层的viewGroup。
// DecorView创建完成了,但还缺少了最重要的一步:把DecorView作为window添加到屏幕上。
在handleResumeActivity中,执行了最后的
wm.addView(mDecor, getWindow().getAttributes());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// 调用Activity的onResume方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
// 让decorView显示到屏幕上
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}直接调用WindowManagerImpl的addView方法来吧decorView添加到屏幕上,至此,我们的Activity界面就会显示在屏幕上了。
进一步需要看WindowManagerImpl.addView之后是怎么将View(即Window)添加入屏幕的
wm.addView(mDecor, getWindow().getAttributes());
其中wm是WindowManagerImpl实例,WindowManagerImpl.addView
其实是通过桥接,调用WindowManagerGlobal的全局单例的方法WindowManagerGlobal.addView
,该方法中会新建一个ViewRootImpl,然后将入参的decorView、新建的ViewRootImp等加入自身维护的mViews、mRoots列表中,同时将DecorView注入ViewRootImplroot.setView(view, wparams, panelParentView)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32WindowMangerGlobal.clss
维护着WMS实例sWindowManagerService和以下列表
//应用所有的decorView
private final ArrayList<View> mViews = new ArrayList<View>();
//应用所有的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//应用所有的WindowManager.LayoutParams
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//... 主要是校验参数和调整子窗口的参数
synchronized (mLock) {
...
// 这里新建了一个viewRootImpl,并设置参数
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 添加到windowManagerGlobal的三个重要list中,每一个window所对应的这三个对象都会保存在这里,之后对window的一些操作就可以直接来这里取对象了。当window被删除的时候,这些对象也会被从list中移除。
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 最后通过viewRootImpl来添加window
try {
root.setView(view, wparams, panelParentView);
}
...
}
}
5. ViewRootImpl.setView()将调用到
1 | res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, |
这个mWindowSession来自于构造器的入参,是由WindowManagerGlobal.getWindowSession中来的,
1 | public static IWindowSession getWindowSession() { |
可以看出,这个session是一个单例,也就是整个应用的所有viewRootImpl的windowSession都是同一个,也就是一个应用只有一个windowSession。
Session.addToDisplay
实际上走的是WMS的addWindow方法,后面的逻辑就交给WMS去处理了,WMS就会创建window,然后结合参数计算window的高度等等,最后使用viewRootImpl进行绘制。
window的添加过程是通过PhoneWindow对应的WindowManagerImpl来添加window,内部会调用WindowManagerGlobal来实现。WindowManagerGlobal会使用viewRootImpl来进行跨进程通信让WMS执行创建window的业务。
每个应用都有一个windowSession,用于负责和WMS的通信,如ApplicationThread与AMS的通信。
Other Window
dialog和popupwindow的层级是10001999,在这个层级都属于子window而不是应用Window(199),子Window需要附属于父Window(Activity,也就是应用Window)才能显示,dialog虽然创建了一个PhoneWindow,但是popupWindow最终也创建了一个Window,只是它不是PhoneWindow而已,popupWindow和dialog的显示都需要依赖父Window的Token,其实两者都需要依赖于Activiy
PopupWindow
那么,PopupWindow则是在构造器中时将入参的contentView直接执行 setContentView(contentView);
,然后在showAtLocation()
中调用了preparePopup()
创建它的decorView(PopupDecorView)之后invokePopup()
调用执行mWindowManager.addView(decorView, p);
- 根据参数构建popupDecorView
- 把popupDecorView添加到屏幕上
dismiss()
中调用mWindowManager.removeViewImmediate(decorView);
Dialog
dialog的创建过程Activity比较像:构造器创建PhoneWindow,setContentView初始化DecorView,show时候添加DecorView。
- 构造函数中创建PhoneWindow,设置WindowManger(这里拿的是传入的context,实际上这context只能是actiivty,的WindowManager)
- Dialog.setContentView时掉PhoneWindow.setContentView来初始化DecorView
show()
时候调用了mWindowManager.addView(mDecor, l);
dismiss()
时候调用mWindowManager.removeViewImmediate(mDecor);
总结
- dialog和popupWindow不同,dialog创建了新的PhoneWindow,使用了PhoneWindow的DecorView模板。而popupWindow没有,popupWindow他也对应一个window,因为它也是通过windowManager添加上去的,不属于Activity的view树。
- dialog的显示层级数更高,会直接显示在Activity上面,在dialog后添加的popUpWindow也会显示在dialog下
- dialog的创建流程和activity非常像
概念梳理//待整理
关键类
前面的源码流程中涉及到很多的类,这里把相关的类统一分析一下。先看一张图:
这基本上是我们这篇文章涉及到的所有关键类。
(图中绿色的window并不是一个类,而是表意上的window)
另外ViewRootImpl并不在PhoneWindow中,而是在View.attachInfo中,也被WindowManagerService中持有维护)
window相关
window的实现类只有一个:PhoneWindow,他继承自Window抽象类。后面我会重点分析他。
WindowManager相关
顾名思义,windowManager就是window管理类。这一部分的关键类有windowManager,viewManager,windowManagerImpl,windowManagerGlobal。windowManager是一个接口,继承自viewManager。viewManager中包含了我们非常熟悉的三个接口:addView,removeView,updateView
。
windowManagerImpl和PhoneWindow是成对出现的,前者负责管理后者。WindowManagerImpl是windowManager的实现类,但是他本身并没有真正实现逻辑,而是交给了WindowManagerGlobal。WindowManagerGlobal是全局单例,windowManagerImpl内部使用桥接模式,他是windowManager接口逻辑的真正实现
view相关
这里有个很关键的类:ViewRootImpl。每个view树都会有一个。当我使用windowManager的addView方法时,就会创建一个ViewRootImpl。ViewRootImpl的作用很关键:
- 负责连接view和window的桥梁事务
- 负责和WindowManagerService的联系
- 负责管理和绘制view树
- 事件的中转站
每个window都会有一个ViewRootImpl,viewRootImpl是负责绘制这个view树和window与view的桥梁,每个window都会有一个ViewRootImpl。
WindowManagerService
这个是window的真正管理者,类似于AMS(ActivityManagerService)管理四大组件。所有的window创建最终都要经过windowManagerService。整个Android的window机制中,WMS绝对是核心,他决定了屏幕所有的window该如何显示如何分发点击事件等等。
从Android架构角度看Window
前面我们介绍过关于PhoneWindow和window之间的关系,了解到PhoneWindow其实不是Window,只是一个window容器。不知读者有没想过一个问题,为什么谷歌要建一个不是window但却名字是window的类?是故意要迷惑我们吗?要了解这个问题,我们先来回顾一下整个android的window机制结构。
首先从WindowManagerService开始,我们知道WMS是window的最终管理者,在WMS中为每一个应用持有一个session,关于session前面我们讲过,每个应用都是全局单例,负责和WMS通信的binder对象。WMS为每个window都建立了一个windowStatus对象,同一个应用的window使用同个session进行跨进程通信,结构大概如下:
而负责与WMS通信的,是viewRootImpl。前面我们讲过每个view树即为一个window,viewRootImpl负责和WMS进行通信,同时也负责view的绘制。如果把上面的图画仔细一点就是:
图中每一个windowStatus对应一个viewRootImpl,WMS通过viewRootImpl来控制view。这也就是window机制的管理结构。当我们需要添加window的时候,最终的逻辑实现是WindowManagerGlobal,他的内部使用自己的session创建一个viewRootImpl,然后向WMS申请添加window,结构图大概如下:
windowManagerGlobal使用自己的IWindowSession创建viewRootImpl,这个IWindowSession是全局单例。viewRootImpl和WMS申请创建window,然后WMS允许之后,再通知viewRootImpl绘制view,同时WMS通过windowStatus存储了viewRootImpl的相关信息,这样如果WMS需要修改view,直接通过viewRootImpl就可以修改view了。
从上面的描述中可以发现我全程没有提及到PhoneWindow和WindowManagerImpl。这是因为他们不属于window机制内的类,而是封装于window机制之上的框架。假设如果没有PhoneWindow和WindowManager我们该如何添加一个window?首先需要调用WindowGlobal获取session,再创建viewRootImpl,再访问wms,然后再利用viewRootImpl绘制view,是不是很复杂,而这仅仅只是整体的步骤。而WindowManagerImpl正是这个功能。他内部拥有WindowManagerGlobal的单例,然后帮助我们完成了这一系列的步骤。同时,windowManagerImpl也是只有一个实例,其他的windowManagerImpl都是建立在windowManagerImpl单例上。这一点在前面有通过源码介绍到。
另外,上面我讲到PhoneWindow并不是window而是一个辅助Activity管理的工具类,那为什么他不要命名为windowUtils呢?首先,PhoneWindow这个类是谷歌给window机制进行更上一层的封装。PhoneWindow内部拥有一个DecorView,我们的布局view都是添加到decorView中的,因为我们可以通过给decorView设置背景,宽高度,标题栏,按键反馈等等,来间接给我们的布局view设置。这样一来,PhoneWindow的存在,向开发者屏蔽真正的window,暴露给开发者一个“存在的”window。我们可以认为PhoneWindow就是一个window,window是view容器。当我们需要在屏幕上添加view的时候,只需要获得应用window对应的windowManagerImpl,然后直接调用addView方法添加view即可。这里也可以解释为什么windowManager的接口方法是addView而不是addWindow,一是window确实是以view的存在形式没错,二是为了向开发者屏蔽真正的window,让我们以为是在往window中添加view,window是真实存在的东西。他们的关系画个图如下:
黄色部分输于谷歌提供给开发者的window框架,而绿色是真正的window机制结构。通过PhoneWindow我们可以很方便地进行window操作,而不须了解底层究竟是如何工作的。PhoneWindow的存在,更是让window的“可见性”得到了实现,让window变成了一个“view容器”。
好了最后来总结一下:
- Android内部的window机制与谷歌暴露给我们的api是不一样的,谷歌封装的目的是为了让我们更好地使用window。
- dialog、popupWindow等框架更是对具体场景进行更进一步的封装。
- 我们在了解window机制的时候,需要跳过应用层,看到window的本质,才能更好地帮助我们理解window。
- 在android的其他地方也是一样,利用封装向开发者屏蔽底层逻辑,让我们更好地运用。但如果我们需要了解他的机制的时候,就需要绕过这层封装,看到本质。
window与PhoneWindow的关系
解释一下标题,window是指window机制中window这个概念,而PhoneWindow是指PhoneWindow这个类。后面我在讲的时候,如果是指类,我会在后面加个‘类’字。如window是指window概念,window类是指window这个抽象类。读者不要混淆。
还记得我在讲window的概念的时候留了一个思考吗?
思考:Android中不是有一个抽象类叫做window还有一个PhoneWindow实现类吗,他们不就是window的存在形式,为什么说window是抽象不存在的
这里我再抛出几个问题:
- 有一些资料认为PhoneWindow就是window,是view容器,负责管理容器内的view,windowManagerImpl可以往里面添加view,如上面我们讲过的addView方法。但是,同时它又说每个window对应一个viewRootImpl,但却没解释为什么每次addView都会新建一个viewRootImpl,前后发送矛盾。
- 有一些资料也是认为PhoneWindow是window,但是他说addView方法不是添加view而是添加window,同时拿这个方法的名字作为论据证明view就是window,但是他没解释为什么在使用addView方法创建window的过程却没有创建PhoneWindow对象。
我们一步步来看。我们首先来看一下源码中对于window抽象类的注释:
复制代码
1 | Abstract base class for a top-level window look and behavior policy. An |
大概意思就是:这个类是顶级窗口的抽象基类,顶级窗口必须继承他,他负责窗口的外观如背景、标题、默认按键处理等。这个类的实例被添加到windowManager中,让windowManager对他进行管理。PhoneWindow是一个top-level window(顶级窗口),他被添加到顶级窗口管理器的顶层视图,其他的window,都需要添加到这个顶层视图中,所以更准确的来说,PhoneWindow并不是view容器,而是window容器。
PhoneWindow是Window吗?
PhoneWindow并不是view容器,而是window容器。
那PhoneWindow的存在意义是什么?
PhoneWindow只是提供了 token 机制来校验子window的合法性
第一、提供DecorView模板。
如下图:
我们的Activity是通过setContentView把布局设置到DecorView中,那么DecorView本身的布局,就成为了Activity界面的背景。同时DecorView是分为标题栏和内容两部分,所以也可以可界面设置标题栏。同时,由于我们的界面是添加在的DecorView中,属于DecorView的一部分。那么对于DecorView的window属性设置也会对我们的布局界面生效。还记得谷歌的官方给window类注释的最后一句话吗:它提供标准的UI策略,如背景、标题区域、默认键处理等。
这些都可以通过DecorView实现,这是PhoneWindow的第一个作用。
第二、抽离Activity中关于window的逻辑。
Activity的职责非常多,如果所有的事情都自己做,那么会造成本身代码极其臃肿。阅读过Activity启动的读者可能知道,AMS也通过ActivityStarter这个类来抽离启动Activity启动的逻辑。这样关于window相关的事情,就交给PhoneWindow去处理了。(事实上,Activity调用的是WindowManagerImpl,但因PhoneWindow和WindowManagerImpl两者是成对存在,他们共同处理window相关的事务,所以这里就简单写成交给PhoneWindow处理。)当Activity需要添加界面时,只需要一句setContentView,调用了PhoneWindow的setContentView方法,就把布局设置到屏幕上了。具体怎么完成,Activity不必管。
第三、PhoneWindow提供了 token 机制来校验子window的合法性
添加window需要有token,而token只有PhoneWindow拥有;因此其他的window都必须直接或间接与Activity的PhoneWindow有联系
PhoneWindow内部有一个token属性,用于验证一个PhoneWindow是否允许添加window。在Activity创建PhoneWindow的时候,就会把从AMS传过来的token赋值给他,从而他也就有了添加token的权限。而其他的PhoneWindow则没有这个权限,因而也无法添加window。
校验 token 的过程主要发生在 WindowManagerService 中。当应用程序请求操作窗口时,比如添加、移动、更新或者移除窗口,系统会通过传递 token 来确定该操作是否合法。如果 token 是无效的,系统将拒绝该操作,校验条件包括
- 权限检查:系统会检查当前应用程序是否有权限执行特定的窗口操作。例如,如果一个应用程序没有 SYSTEM_ALERT_WINDOW 权限,它就无法显示悬浮窗口。
- 安全性检查:系统可能会检查窗口的 token 是否有效、是否来自可信任的应用程序等。这可以防止恶意应用程序伪造窗口 token,尝试执行未经授权的窗口操作。
- 焦点和层级检查:系统可能会检查窗口的焦点状态和层级关系,以确保窗口的显示不会干扰到其他窗口或系统功能的正常运行。
附录 常见type&flag
AndroidDeveloper.Window flag and type
window的type属性
前面我们讲到window机制解决的一个问题就是view的显示次序问题,这个属性就决定了window的显示次序。window是有分类的,不同类别的显示高度范围不同,例如我把1-1000m高度称为低空,1001-2000m高度称为中空,2000以上称为高空。window也是一样按照高度范围进行分类,他也有一个变量Z-Order,决定了window的高度。window一共可分为三类:
- 应用程序窗口:应用程序窗口一般位于最底层,Z-Order在1-99
- 子窗口:子窗口一般是显示在应用窗口之上,Z-Order在1000-1999
- 系统级窗口:系统级窗口一般位于最顶层,不会被其他的window遮住,如Toast,Z-Order在2000-2999。如果要弹出自定义系统级窗口需要动态申请权限。
Z-Order越大,window越靠近用户,也就显示越高,高度高的window会覆盖高度低的window。
window的type属性就是Z-Order的值,我们可以给window的type属性赋值来决定window的高度。系统为我们三类window都预设了静态常量,如下(以下常用参数介绍转自参考文献第一篇文章):
应用级window
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 应用程序 Window 的开始值
public static final int FIRST_APPLICATION_WINDOW = 1;
// 应用程序 Window 的基础值
public static final int TYPE_BASE_APPLICATION = 1;
// 普通的应用程序
public static final int TYPE_APPLICATION = 2;
// 特殊的应用程序窗口,当程序可以显示 Window 之前使用这个 Window 来显示一些东西
public static final int TYPE_APPLICATION_STARTING = 3;
// TYPE_APPLICATION 的变体,在应用程序显示之前,WindowManager 会等待这个 Window 绘制完毕
public static final int TYPE_DRAWN_APPLICATION = 4;
// 应用程序 Window 的结束值
public static final int LAST_APPLICATION_WINDOW = 99;子window
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 子 Window 类型的开始值
public static final int FIRST_SUB_WINDOW = 1000;
// 应用程序 Window 顶部的面板。这些 Window 出现在其附加 Window 的顶部。
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
// 用于显示媒体(如视频)的 Window。这些 Window 出现在其附加 Window 的后面。
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
// 应用程序 Window 顶部的子面板。这些 Window 出现在其附加 Window 和任何Window的顶部
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
// 当前Window的布局和顶级Window布局相同时,不能作为子代的容器
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
// 用显示媒体 Window 覆盖顶部的 Window, 这是系统隐藏的 API
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
// 子面板在应用程序Window的顶部,这些Window显示在其附加Window的顶部, 这是系统隐藏的 API
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
// 子 Window 类型的结束值
public static final int LAST_SUB_WINDOW = 1999;系统级window
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// 系统Window类型的开始值
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;
// 已经从系统中被移除,可以使用 TYPE_KEYGUARD_DIALOG 代替
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
// 系统对话框窗口
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
// 锁屏时显示的对话框
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
// 输入法窗口,位于普通 UI 之上,应用程序可重新布局以免被此窗口覆盖
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
// 输入法对话框,显示于当前输入法窗口之上
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
// 墙纸
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
// 状态栏的滑动面板
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
// 应用程序叠加窗口显示在所有窗口之上
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
// 系统Window类型的结束值
public static final int LAST_SYSTEM_WINDOW = 2999;
Window的flags参数
flag标志控制window的东西比较多,很多资料的描述是“控制window的显示”,但我觉得不够准确。flag控制的范围包括了:各种情景下的显示逻辑(锁屏,游戏等)还有触控事件的处理逻辑。控制显示确实是他的很大部分功能,但是并不是全部。下面看一下一些常用的flag,就知道flag的功能了(以下常用参数介绍转自参考文献第一篇文章):
复制代码
1 | // 当 Window 可见时允许锁屏 |
window的solfInputMode属性
这一部分就是当软件盘弹起来的时候,window的处理逻辑,这在日常中也经常遇到,如:我们在微信聊天的时候,点击输入框,当软键盘弹起来的时候输入框也会被顶上去。如果你不想被顶上去,也可以设置为被软键盘覆盖。下面介绍一下常见的属性(以下常见属性介绍选自参考文献第一篇文章):
复制代码
1 | // 没有指定状态,系统会选择一个合适的状态或者依赖于主题的配置 |
window的其他属性
上面的三个属性是window比较重要也是比较复杂 的三个,除此之外还有几个日常经常使用的属性:
- x与y属性:指定window的位置
- alpha:window的透明度
- gravity:window在屏幕中的位置,使用的是Gravity类的常量
- format:window的像素点格式,值定义在PixelFormat中
如何给window属性赋值
window属性的常量值大部分存储在WindowManager.LayoutParams类中,我们可以通过这个类来获得这些常量。当然还有Gravity类和PixelFormat类等。
一般情况下我们会通过以下方式来往屏幕中添加一个window:
复制代码
1 | // 在Activity中调用 |
我们可以直接给WindowManager.LayoutParams对象设置属性。
第二种赋值方法是直接给window赋值,如
复制代码
1 | getWindow().flags = WindowManager.LayoutParams.FLAG_FULLSCREEN; |
除此之外,window的solfInputMode属性比较特殊,他可以直接在AndroidManifest中指定,如下:
复制代码
1 | <activity android:windowSoftInputMode="adjustNothing" /> |
最后总结一下:
- window的重要属性有type、flags、solfInputMode、gravity等
- 我们可以通过不同的方式给window属性赋值
- 没必要去全部记下来,等遇到需求再去寻找对应的常量即可
未整理
一个Activity或一个Dialog对应一个PhoneWindow,一个PhoneWindow对应一个WindowManger,对应一个WindowManagerImpl
所以得
- mChoreographer,Choreographer的实例,在SampleWindow的例子中已经见过了。Choreographer的意思是编舞指导。它拥有从显示子系统获取VSYNC同步事件的能力,从而可以在合适的时机通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致的画面撕裂。从这个意义上讲,Choreographer的确是指导Android翩翩起舞的大师。WMS使用Choreographer负责驱动所有的窗口动画、屏幕旋转动画、墙纸动画的渲染。
- mAnimator,WindowAnimator的实例。它是所有窗口动画的总管(窗口动画是一个WindowStateAnimator对象)。在Choreographer的驱动下,逐个渲染所有的动画。
- mPolicy,WindowPolicyManager的一个实现。目前它只有PhoneWindowManager一个实现类。mPolicy定义了很多窗口相关的策略,可以说是WMS的首席顾问!每当WMS要做什么事情的时候,都需要向这个顾问请教应当如何做。例如,告诉WMS某一个类型的Window的ZOrder的值是多少,帮助WMS矫正不合理的窗口属性,会为WMS监听屏幕旋转的状态,还会预处理一些系统按键事件(例如HOME、BACK键等的默认行为就是在这里实现的),等等。所以,mPolicy可谓是WMS中最重要的一个成员了。
- mDisplayContents,一个DisplayContent类型的列表。Android 4.2支持基于Wi-Fi Display的多屏幕输出,而一个DisplayContent描述了一块可以绘制窗口的屏幕。每个DisplayContent都用一个整型变量作为其ID,其中手机默认屏幕的ID由Display.DEFAULT_DISPLAY常量指定。DisplayContent的管理是由DisplayManagerService完成的,在本章不会去探讨DisplayContent的实现细节,而是关注DisplayContent对窗口管理与布局的影响
- mTokenMap,一个HashMap,保存了所有的显示令牌(类型为WindowToken),用于窗口管理。在SampleWindow例子中曾经提到过,一个窗口必须隶属于某一个显示令牌。在那个例子中所添加的令牌就被放进了这个HashMap中。从这个成员中还衍生出几个辅助的显示令牌的子集,例如mAppTokens保存了所有属于Activity的显示令牌(WindowToken的子类AppWindowToken),mExitingTokens则保存了正在退出过程中的显示令牌等。其中mAppTokens列表是有序的,它与AMS中的mHistory列表的顺序保持一致,反映了系统中Activity的顺序。
- mWindowMap,也是一个HashMap,保存了所有窗口的状态信息(类型为WindowState),用于窗口管理。在SampleWindow例子中,使用IWindowSession.add()所添加的窗口的状态将会被保存在mWindowMap中。与mTokenMap一样,mWindowMap一样有衍生出的子集。例如mPendingRemove保存了那些退出动画播放完成并即将被移除的窗口,mLosingFocus则保存了那些失去了输入焦点的窗口。在DisplayContent中,也有一个windows列表,这个列表存储了显示在此Display-Content中的窗口,并且它是有序的。窗口在这个列表中的位置决定了其最终显示时的Z序。
- mSessions,一个List,元素类型为Session。Session其实是SampleWindow例子中的IWindowSession的Bn端。也就是说,mSessions这个列表保存了当前所有想向WMS寻求窗口管理服务的客户端。注意Session是进程唯一的。
- mRotation,只是一个int型变量。它保存了当前手机的旋转状态
Android
系统采用一种称为 Surface
的图形架构,简而言之,每一个 Activity
都关联有至少一个 Window
(窗口),每一个 Window
都对应有一个 Surface
。
Surface
这里直译过来叫做 绘图表面 ,顾名思义,其可在内存中生成一个图形缓冲区队列,用于描述 UI
,经与系统服务的WindowServiceManager
通信后、通过 SurfaceFlinger
服务持续合成并送显到显示屏。
由此可见,通常情况下,一个 Activity
的 UI
渲染本质是 系统提供一块内存,并创建一个图形缓冲区进行维护;这块内存就是 Surface
,最终页面所有 View
的 UI
状态数据,都会被填充到同一个 Surface
中。