WindowManagerService(以后简称WMS)它是 WindowManager 的管理者, WMS 无论对于应用开发还是 Framework 开发都是重要的知识点,究其原因是因为 WMS 有很多职责,每个职责都会涉及重要且复杂的系统。 WMS 的职责主要有以下几点。
1. 窗口管理:负责窗口的启动、添加和删除。另外窗口的大小和层级也是由WMS进行管理的。窗口管理的核心成员有DisplayContent、WindowToken和WindowState。
2. 窗口动画:在进行窗口切换时,使用窗口动画可以显得更炫一些,窗口动画由 WMS 的动画子系统来负责,动画子系统的管理者为 WindowAnimator。
3. 输入系统的中转站:通过对窗口的触摸从而产生触摸事件, InputManagerService (IMS )会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息, WMS 是窗口的管理者,它作为输入系统的中转站再合适不过了。
4. Surface管理:窗口并不具备绘制的功能,因此每个窗口都需要有一块 Surface 来供自己绘制,为每个窗口分配 Surface 是由 WMS 来完成的。
本文将基于Android8.1.0系统来详细讲解WMS。
WMS是在SystemServer进程中创建的,不了解SystemServer进程的可以查看“Android系统启动流程(3) —— 解析SystemServer进程启动过程”这篇文章,我们知道官方把系统服务分为了引导服务、核心服务和其他服务,其中其他服务是一些非紧要和不需要立即启动的服务, WMS 就是其他服务的一种 。我们来查看在SystemServer进程中的 startOtherServices 方法中是如何启动 WMS 的,代码如下所示:
frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
try {
...
traceBeginAndSlog("InitWatchdog");
final Watchdog watchdog = Watchdog.getInstance(); // ... 1
watchdog.init(context, mActivityManagerService); // ... 2
traceEnd();
traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context); // ... 3
traceEnd();
traceBeginAndSlog("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager()); // ... 4
ServiceManager.addService(Context.WINDOW_SERVICE, wm); // ... 5
ServiceManager.addService(Context.INPUT_SERVICE, inputManager); // ... 6
traceEnd();
...
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
}
...
traceBeginAndSlog("MakeDisplayReady");
try {
wm.displayReady(); // ... 7
} catch (Throwable e) {
reportWtf("making display ready", e);
}
traceEnd();
...
traceBeginAndSlog("MakeWindowManagerServiceReady");
try {
wm.systemReady(); // ... 8
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
traceEnd();
...
}
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0); // ... 1
return sInstance;
}
通过调用 DisplayThread 的 getHandler 方法来得到 DisplayThread 的 Handler 实例。DisplayThread 是一个单例的前台线程,这个线程用来处理需要低延时显示的相关操作, 并只能由 WindowManager、 DisplayManager 和 InputManager 实时执行快速操作 。在注释1处创建了 WMS 实例,这个过程运行在 Runnable 的 run 方法中,而 Runnable 则传到了 DisplayThread 对应 Handler 的 runWithScissors 方法中,说明 WMS 的创建是运行 android.display 中的 。需要注意的是, runWithScissors 方法的第二个参数传入的是0,后面会提到, 下面来查看 Handler 的 runWithScissors 方法,代码如下所示:
frameworks/base/core/java/android/os/Handler.java
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) { // ... 1
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run(); // ... 1
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) { // ... 2
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait(); // ... 3
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
...
// Must be before createDisplayContentLocked.
mInputManager = inputManager; // ... 1
...
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays(); // ... 2
for (Display display : mDisplays) {
createDisplayContentLocked(display); // ... 3
}
...
mActivityManager = ActivityManager.getService(); // ... 4
...
mAnimator = new WindowAnimator(this); // ... 5
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy(); // ... 6
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this); // ... 7
openSurfaceTransaction();
try {
createWatermarkInTransaction();
} finally {
closeSurfaceTransaction();
}
showEmulatorDisplayOverlayIfNeeded();
}
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); // ... 1
}
}, 0);
}
1. mPolicy:WindowManagerPolicy
mPolicy是WindowManagerPolicy(WMP)类型的变量。WMP是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了 WindowManager 所有的特定的 UI 行为 。它的具体实现类为 PhoneWindowManager ,这个实现类在 WMS 创建时被创建。 WMP 允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
2. mSessions: ArraySet
mSessions 是 ArraySet 类型的变量,元素类型为 Session ,它主要用于进程间通信,其他的应用程序进程想要和 WMS 进程进行通信就需要经过 Session ,并且每个应用程序进程都会对应一个 Session, WMS 保存这些 Session 用来记录所有向 WMS 提出窗口管理服务的客户端。
Window的操作分为两大部分,一部分是WindowManger处理部分,另一部分是WMS处理部分,在“解析WindowManager”中讲过Window 添加过程的 WindowManager 处理部分,这里我们接着来分析 Window 的添加过程的 WMS 处理部分。无论是系统 口还是 Activity ,它们的 Window 的添加过程都会调用 WMS 的 addWindow方法,代码如下所示
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp); // ... 1
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); // ... 2
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
...
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { // ... 3
parentWindow = windowForClientLocked(null, attrs.token, false); // ... 4
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
...
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token); // ... 5
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type; // ... 6
boolean addToastWindowRequiresToken = false;
if (token == null) {
...
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow); // ... 7
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { // ... 8
atoken = token.asAppWindowToken(); // ... 9
}
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow); // ... 10
if (win.mDeathRecipient == null) { // ... 11
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) { // ... 12
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
mPolicy.adjustWindowParamsLw(win.mAttrs); // ... 13
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs); // ... 14
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
win.attach();
mWindowMap.put(client.asBinder(), win); // ... 15
if (win.mAppOp != AppOpsManager.OP_NONE) {
int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
(startOpResult != AppOpsManager.MODE_DEFAULT)) {
win.setAppOpVisibilityLw(false);
}
}
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
final AppWindowToken aToken = token.asAppWindowToken();
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
boolean imMayMove = true;
win.mToken.addWindow(win); // ... 16
...
}
...
return res;
}
在注释5处通过 displayContent 的 getWindowToken 方法得到 WindowToken 。
在注释6处如果有父窗口就将父窗口的 type 值赋值给 rootType ,如果没有将当前窗口的 type 值赋值给 rootType 。接下来如果 WindowToken 为 null ,则根据 rootType 或者 type 的值进行区分判断。
在注释7处隐式创建 WindowToken ,这说明当我们添加窗口时可以不向 WMS 提供 WindowToken ,前提是 rootType 的 type 的值不为前面条件判断筛选的值。 WindowToken 隐式和显式的创建肯定是要加以区分的。注释7处的第4个参数为 false 就代表这个 WindowToken 是隐式创建的。
接下来的代码逻辑就是 WindowToken 不为 null 的情况,根据 rootType 和 type 的值进行判断,比如在注释8处判断如果窗口为应用程序窗口,在注释9处将 WindowToken 转换为专门针对应用程序窗口的 AppWindowToken ,然后根据 AppWindowToken 的值进行后续的判断。
在注释10处创建了 WindowState ,它存有窗口的所有的状态信息,在 WMS 中它代表一个窗口。在创建 WindowState 传入的参数中, this 指的是 WMS,client 指的是IWindow,IWindow 会将 WMS 中窗口管理的操作回调给 ViewRootlmpl,,token 指的是 WindowToken ,
在注释11和注释12处分别判断请求添加窗口的客户端是否已经死亡、 窗口的 DisplayContent 是否 null ,如果是则不再执行下面的代码逻辑。
在注释13处调用了 WMP 的 adjustWindowParamsLw 方法, 改方法在 PhoneWindowManager 中实现,此方法会根据窗口的 type 对窗口的 LayoutParams 的一些成员变量进行修改。
在注释14处调用 WMP 的 prepareAddWindowLw 方法,用于准备将窗口添加到系统中。
在注释15处将 WindowState 添加到 mWindowMap 中。
在注释16处将 WindowState 添加到该 WindowState 对应的 WindowToken 中(实际是保存在 WindowToken 的父类 WindowContainer 中),这样 WindowToken 就包含了同一个组件的 WindowState。
addWindow方法总结:
(1) 对所要添加的窗口进行检查,如过窗口不满足一些条件,就不再执行下面的代码逻辑。
(2) WindowToken 相关的处理,比如有的类型需要提供 WindowToken ,没有提供的话就不执行下面的代码逻辑,有的窗口类型需要由 WMS 隐式创建 WindowToken。
(3) WindowState 的创建和相关处理,将 WindowToken WindowState 相关联。
(4) 创建和配置 DisplayContent,完成窗口添加到系统前的准备工作。
Window的删除过程与Window的创建和更新过程一样,要删除Window需要先调用 WindowManagerImpl 的 removeView 方法,在 removeView 方法中又会调用 WindowManagerGlobal 的 removeView 方法,然后就从这里开始分析起。WindowManagerGlobal 的 removeView 方法如下所示:
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true); // ... 1
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate); // ... 2
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
frameworks/base/core/java/android/view/WindowManagerGlobal.java
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index); // ... 1
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance(); // ... 2
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());// ... 3
}
}
boolean deferred = root.die(immediate); // ... 4
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
在注释1处根据传入的索引在 ViewRootlmpl 列表中获得 ViewRootlmpl 。在注释 2 处得到 InputMethodManager 实例,如 InputMethodManager 实例不为 null ,则在注释 3 处调用 InputMethodManager 的 windowDismissed 方法来结束要删除Window(View)的输入法相关的逻辑。在注释4处调用 ViewRootlmpl 的 die 方法,如下所示:
frameworks/base/core/java/android/view/ViewRootlmpl.java
boolean die(boolean immediate) {
// die方法需要立即执行并且此时ViewRootImpl不再执行performTraversals方法
if (immediate && !mIsInTraversal) { // ... 1
doDie(); // ... 2
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
frameworks/base/core/java/android/view/ViewRootlmpl.java
void doDie() {
// 检查执行doDie方法的线程的正确性
checkThread(); // ... 1
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) { // ... 2
return;
}
mRemoved = true; // ... 3
if (mAdded) { // ... 4
dispatchDetachedFromWindow(); // ... 5
}
if (mAdded && !mFirst) { // ... 6
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this); // ... 7
}
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root); // ... 1
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
void dispatchDetachedFromWindow() {
...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
...
}
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false); // ... 1
if (win == null) {
return;
}
win.removeIfPossible(); // ... 2
}
}
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
...
removeImmediately(); // ... 1
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
@Override
void removeImmediately() {
super.removeImmediately();
if (mRemoved) { // ... 1
// Nothing to do.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"WS.removeImmediately: " + this + " Already removed...");
return;
}
mRemoved = true; // ... 2
...
mPolicy.removeWindowLw(this); // ... 3
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
mSession.windowRemovedLocked(); // ... 4
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
// Ignore if it has already been removed (usually because
// we are doing this as part of processing a death note.)
}
mService.postWindowRemoveCleanupLocked(this); // ... 5
}