文章目录
- [WMS 及其成员](#WMS 及其成员)
-
- [WMS 的职责](#WMS 的职责)
- WMS的重要成员
- [Window 相关类](#Window 相关类)
-
- [Window、WindowManager 和 WMS 的关系](#Window、WindowManager 和 WMS 的关系)
- [WindowManager 相关方法](#WindowManager 相关方法)
- [PhoneWindow 相关方法](#PhoneWindow 相关方法)
- [Window 的属性](#Window 的属性)
-
- [Window 的类型和显示次序](#Window 的类型和显示次序)
- [Window 的标志](#Window 的标志)
- 软键盘相关模式
基于Android U
WMS 及其成员
WMS 的职责

- 窗口管理
WMS 是窗口的管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由 WMS 进行管理的。窗口管理的核心成员有 DisplayContent、WindowToken 和 WindowState。
- 窗口动画
窗口间进行切换时,使用窗口动画可以显得更炫一些,窗口动画由WMS的动画子系统来负责,动画子系统的管理者为 WindowAnimator。
- 输入系统的中转站
通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,它作为输入系统的中转站再合适不过了。
- Surface 管理
窗口并不具备绘制的功能,因此每个窗口都需要有一块 Surface 来供自己绘制,为每个窗口分配 Surface 是由 WMS 来完成的。
WMS的重要成员
- mPolicy: WindowManagerPolicy
mPolicy 是 WindowManagerPolicy(WMP)类型的变量。WindowManagerPolicy 是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了 WindowManager 所有的特定的 UI 行为。它的具体实现类为 PhoneWindowManager,这个实现类在 WMS 创建时被创建。WMP 允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
- mSessions: ArraySet
mSessions 是 ArraySet 类型的变量,元素类型为 Session,它主要用于进程间通信,其他的应用程序进程想要和 WMS 进程进行通信就需要经过 Session,并且每个应用程序进程都会对应一个 Session,WMS 保存这些 Session 用来记录所有向 WMS 提出窗口管理服务的客户端。
- mWindowMap: HashMap
mWindowMap 是 HashMap 类型的变量,用来保存 WMS 中各种窗口的集合。key 值的类型为 IBinder,value 值的类型为 WindowState。WindowState 用于保存窗口的信息,在 WMS 中它用来描述一个窗口。
- mResizingWindows: ArrayList
mResizingWindows 是 ArrayList 类型的变量,元素类型为 WindowState。mResizingWindows 是用来存储正在调整大小的窗口的列表。与 mResizingWindows 类型的还有 mForceRemoves、mDestroySurface 等,其中mForceRemoves 是在内存耗尽时设置的,里面存有需要强制删除的窗口,mDestroySurface 里面存有需要被销毁的 Surface。
- mAnimator: WindowAnimator
mAnimator 是 WindowAnimator 类型的变量,用于管理窗口的动画以及特效动画。
- mH: H
mH 是 H 类型的变量,系统的 Handler 类,用于将任务加入到主线程的消息队列中,这样代码逻辑就会在主线程中执行。
- mInputManager: InputManagerService
mInputManager 是 InputManagerService 类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,因此 WMS 作为输入系统的中转站是再合适不过了。
Window 相关类
Window、WindowManager 和 WMS 的关系
Window 是一个抽象类,具体的实现类为 PhoneWindow,它对 View 进行管理。
WindowManager 是一个接口类,继承自接口 ViewManager,它是用来管理 Window 的,它的实现类为 WindowManagerImpl。如果我们想要对 Window(View)进行添加、更新和删除操作就可以使用 WindowManager,WindowManager 会将具体的工作交由 WMS 来处理,WindowManager 和 WMS 通过 Binder 来进行跨进程通信,WMS 作为系统服务有很多 API 是不会暴露给 WindowManager 的。

Window 包含了 View 并对 View 进行管理,Window 用虚线来表示是因为 Window 是一个抽象概念,用来描述一个窗口,并不是真实存在的,Window 的实体其实也是 View。WindowManager 用来管理 Window,而 WindowManager 所提供的功能最终会由 WMS 进行处理。
WindowManager 相关方法
WindowManager 是一个接口类,继承自接口 ViewManager,ViewManager 中定义了三个方法,分别用来添加、更新和删除 View。
java
frameworks/base/core/java/android/view/ViewManager.java
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
WindowManager 也继承了这些方法,而这些方法传入的参数都是 View 类型,说明 Window 是以 View 的形式存在的。WindowManager 在继承 ViewManager 的同时,又加入很多功能,包括 Window 的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据 Window 的特性加入的:
java
frameworks/base/core/java/android/view/WindowManager.java
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
getDefaultDisplay() 方法能够得知这个 WindowManager 实例将 Window 添加到哪个屏幕上了,换句话说,就是得到 WindowManager 所管理的屏幕(Display)。
removeViewImmediate() 方法则规定在这个方法返回前要立即执行 View.onDetachedFromWindow(),来完成传入的 View 相关的销毁工作。
PhoneWindow 相关方法
PhoneWindow 是在 Activity 创建的 attach() 方法中创建的。
java
frameworks/base/core/java/android/app/Activity.java
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback); // 1
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); // 2
...
}
注释1处创建了 PhoneWindow,注释2处调用 PhoneWindow#setWindowManager() 方法,这个方法在 PhoneWindow 的父类 Window 中实现。
java
frameworks/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); // 1
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); // 2
}
-
如果传入的 WindowManager 为 null,就会在注释1处调用 Context#getSystemService() 方法,并传入服务的名称 Context。WINDOW_SERVICE(值为 window),具体在 ContextImpl 中实现。
javaframeworks/base/core/java/android/app/ContextImpl.java public Object getSystemService(String name) { ... return SystemServiceRegistry.getSystemService(this, name); }调用 SystemServiceRegistry#getSystemService() 方法。
javaframeworks/base/core/java/android/app/SystemServiceRegistry.java public static Object getSystemService(ContextImpl ctx, String name) { ... final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); ... final Object ret = fetcher.getService(ctx); ... return ret; }SYSTEM_SERVICE_FETCHERS 是一个 ArrayMap,其 Key 为系统服务名,Value 为 ServiceFetcher 对象。
-
SYSTEM_SERVICE_FETCHERS 是在什么时候被赋值的?
javaframeworks/base/core/java/android/app/SystemServiceRegistry.java private static <T> void registerService(@NonNull String serviceName, @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName()); } -
registerService() 方法是什么时候被调用的?
javaframeworks/base/core/java/android/app/SystemServiceRegistry.java static { ... registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); // 1 }}); ... }在 SystemServiceRegistry 的静态代码块中会调用多个 registerService() 方法,registerService() 方法内部会将传入的服务的名称存入到 SYSTEM_SERVICE_FETCHERS 中。从注释1处可以看出,传入的 Context.WINDOW_SERVICE 对应的就是 WindowManagerImpl 实例。
-
-
mContext.getSystemService() 得到 WindowManagerImpl 实例后转为 WindowManager 类型,在注释2处调用了 WindowManagerImpl#createLocalWindowManager() 方法。
javaframeworks/base/core/java/android/view/WindowManagerImpl.java public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken); }createLocalWindowManager() 方法同样也是创建 WindowManagerImpl,不同的是这次创建 WindowManagerImpl 时将创建它的 Window 作为参数传了进来,这样 WindowManagerImpl 就持有了 Window 的引用,可以对 Window 进行操作,比如在 Window 中添加 View,会调用 WindowManagerImpl 的 addView() 方法,如下所示:
javaframeworks/base/core/java/android/view/WindowManagerImpl.java public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { android.util.SeempLog.record_vg_layout(383,params); applyTokens(params); mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); // 1 }注释1处调用了 WindowManagerGlobal 的 addView() 方法,其中参数 mParentWindow 就是上面提到的 Window,可以看出 WindowManagerImpl 虽然是 WindowManage 的实现类,但是没有实现什么功能,而是将功能实现委托给了 WindowManagerGlobal,这里用到的是桥接模式。
我们来查看 WindowManagerImpl 中是如何定义 WindowManagerGlobal 的。
java
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); // 1
@UiContext
@VisibleForTesting
public final Context mContext;
private final Window mParentWindow; // 2
private WindowManagerImpl(Context context, Window parentWindow,
@Nullable IBinder windowContextToken) {
mContext = context;
mParentWindow = parentWindow; // 3
mWindowContextToken = windowContextToken;
mWindowMetricsController = new WindowMetricsController(mContext);
}
}
注释1处可以看出 WindowManagerGlobal 是一个单例,说明在一个进程中只有一个 WindowManagerGlobal 实例。
注释2处结合注释3处说明这个 WindowManagerImpl 实例会作为哪个 Window 的子 Window,这也就说明在一个进程中 WindowManagerImpl 可能会有多个实例。
通过如上的源码分析,WindowManager 的关联类如下图所示:

从图中可以看出,PhoneWindow 继承自 Window,Window 通过 setWindowManager() 方法与 WindowManager 发生关联。WindowManager 继承自接口 ViewManager,WindowManagerImpl 是 WindowManager 接口的实现类,但是具体的功能都会委托给 WindowManagerGlobal 来实现。
Window 的属性
WMS 是 Window 的最终管理者,Window 好比是员工,WMS 是老板,为了方便老板管理员工则需要定义一些"协议",这些"协议"就是 Window 的属性,它们被定义在 WindowManager 的内部类 LayoutParams 中,了解 Window 的属性能够更好地理解 WMS 的内部原理。Window 的属性有很多种,与应用开发最密切的有三种,分别是 Type(Window 的类型)、Flag(Window 的标志)和 SoftInputMode(软键盘相关模式),下面分别介绍这三种 Window 的属性。
Window 的类型和显示次序
Window 的类型有很多种,比如应用程序窗口、系统错误窗口、输入法窗口、PopupWindow、Toast、Dialog等。总的来说,Window 分为三大类型,分别是Application Window(应用程序窗口)、Sub Window(子窗口)、System Window(系统窗口),每个大类型中又包含了很多种类型,它们都定义在 WindowManager 的静态内部类 LayoutParams 中。
- 应用程序窗口
Activity 就是一个典型的应用程序窗口,应用程序窗口包含的类型如下所示:
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;
// 应用程序窗口类型结束值
public static final int LAST_APPLICATION_WINDOW = 99;
应用程序窗口包含了以上几种 Type 值,应用程序窗口的 Type 值范围为1~99,这个数值的大小涉及窗口的层级。
- 子窗口
子窗口,顾名思义,它不能独立存在,需要附着在其他窗口才可以, PopupWindow 就属于子窗口。子窗口的类型定义如下所示:
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;
可以看出子窗口的 Type 值范围为1000~1999。
- 系统窗口
Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。系统窗口的类型定义如下所示:
java
// 系统窗口类型初始值
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;
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
...
// 系统窗口类型结束值
public static final int LAST_SYSTEM_WINDOW = 2999;
这里只列出部分系统窗口的类型值,系统窗口的 Type 值范围为2000~2999。
- 窗口显示次序
当一个进程向 WMS 申请一个窗口时,WMS 会为窗口确定显示次序。为了方便窗口显示次序的管理,手机屏幕可以虚拟地用 X、Y、Z 轴来表示,其中 Z 轴垂直于屏幕,从屏幕内指向屏幕外,这样确定窗口显示次序也就是确定窗口在 Z 轴上的次序,这个次序称为 Z-Oder。Type 值是 Z-Oder 排序的依据,我们知道应用程序窗口的 Type 值范围为1~99,子窗口1000~1999,系统窗口2000~2999,在一般情况下,Type 值越大则 Z-Oder 排序越靠前,就越靠近用户。当多个窗口的 Type 值相同,WMS 会结合各种情况给出最终的 Z-Oder。
Window 的标志
Window 的标志也就是 Flag,用于控制 Window 的显示,同样被定义在 WindowManager 的内部类 LayoutParams 中。这里列出几个常用的:
| 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_ON | 只要窗口可见,屏幕就会一直亮着 |
| FLAG_LAYOUT_NO_LIMITS | 允许窗口超过屏幕之外 |
| FLAG_FULLSCREEN | 隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示 |
| FLAG_SHOW_WHEN_LOCKED | 窗口可以在锁屏的窗口之上显示 |
| FLAG_IGNORE_CHEEK_PRESSES | 当用户的脸贴近屏幕时(比如打电话),不会去响应此事件 |
| FLAG_TURN_SCREEN_ON | 窗口显示时将屏幕点亮 |
设置 Window 的 Flag 有三种方法。
-
通过 Window 的 addFlags() 方法。
javaWindow mWindow = getWindow(); mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); -
通过 Window 的 setFlags() 方法。
javaWindow mWindow = getWindow(); mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);其实 Window 的 addFlags() 方法内部会调用 setFlags() 方法,因此这两种方法区别不大。
-
给 LayoutParams 设置 Flag,并通过 WindowManager 的 addView() 方法进行添加。
javaWindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN; WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); TextView mTextView = new TextView(this); mWindowManager.addView(mTextView, mWindowLayoutParams);
软键盘相关模式
窗口和窗口的叠加是十分常见的场景,但如果其中的窗口是软键盘窗口,可能就会出现一些问题,比如典型的用户登录界面,默认的情况弹出的软键盘窗口可能会盖住输入框下方的按钮,这样用户体验会非常糟糕。为了使得软键盘窗口能够按照期望来显示,WindowManager 的静态内部类 LayoutParams 中定义了软键盘相关模式。
| SoftInputMode | 描述 |
|---|---|
| SOFT_INPUT_STATE_UNSPECIFIED | 没有指定状态,系统会选择一个合适的状态或依赖于主题的设置 |
| SOFT_INPUT_STATE_UNCHANGED | 不会改变软键盘状态 |
| SOFT_INPUT_STATE_HIDDEN | 当用户进入该窗口时,软键盘默认隐藏 |
| SOFT_INPUT_STATE_ALWAYS_HIDDEN | 当窗口获取焦点时,软键盘总是被隐藏 |
| SOFT_INPUT_ADJUST_RESIZE | 当软键盘弹出时,窗口会调整大小 |
| SOFT_INPUT_ADJUST_PAN | 当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的 |
从上面给出的 SoftInputMode,可以发现,它们与 AndroidManifest 中 Activity 的属性 android:windowSoftInputMode 是对应的。因此除了在AndroidManifest中为 Activity 设置 android:windowSoftInputMode 以外,还可以在 Java 代码中为 Window 设置 SoftInputMode,如下所示:
java
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);