这篇从 Window 的增删过程中看看 WMS 的工作(由于主要从代码上整理,所以代码展示比较多,建议用大屏幕阅读)
addView
addView 的操作从 WindowManagerGlobal 开始经过 ViewRootImpl 再经过 Session 最后就会调用 WMS 的添加窗口方法 addWindow。
public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {
//做权限校验,如果不通过就直接结束返回了
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
WindowState parentWindow = null;
final int type = attrs.type;
synchronized (mGlobalLock) {
//找到指定的屏幕,DisplayContent 用来描述屏幕信息
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//如果窗口类型为子窗口,那么需要获取到相关联的父窗口
parentWindow = windowForClientLocked(null, attrs.token, false);
}
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
//根据是否有父窗口来获取 token 信息,这个在前面说过,表示窗口令牌
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
if (token == null) {
//如果 token 没有,就会根据 rootType 进行一系列判断校验
//隐式创建 token
token = new WindowToken(this, binder, type, false, displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//如果 token 不为 null,且窗口类型是应用窗口,就做一层转换
//转换成 AppWindowToken 类型
atoken = token.asAppWindowToken();
} //后面还有很多其他 case
//创建一个 WindowState 对象,这个在 WMS 里就代表着一个窗口
final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], seq, attrs, viewVisibility, session.mUid, session.mCanAddInternalSystemWindow);
//与书中的描述不同,在 API 29 中似乎从 WMP 中分离出了 DisplayPolicy 用于窗口属性的调整
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid());
//准备好添加窗口
res = displayPolicy.prepareAddWindowLw(win, attrs);
win.attach();
//将这个窗口放入 HashMap 集合中
mWindowMap.put(client.asBinder(), win);
//这是 WindowToken 的另外一个功能,将同一个组件的 WindowState 集合管理
win.mToken.addWindow(win);
}
return res;
}
removeView
按照书中的分析思路,WMS 添加窗口的主要逻辑就像上面代码描述的这样。接下来再来看看删除窗口的主要逻辑,
//WindowManagerGlobal
public void removeView(View view, boolean immediate) {
synchronized (mLock) {
//从 mViews 中找到要移除的窗口下标
int index = findViewLocked(view, true);
//找到相对应的 ViewRootImpl 对象,如果不匹配会在后面抛异常
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
//前面方法说了,这个 view 就是要移除的窗口
View view = root.getView();
if (view != null) {
//结束和这个窗口相关的输入法操作
InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
if (deferred) {
mDyingViews.add(view);
}
}
}
//ViewRootImpl
boolean die(boolean immediate) {
//如果要求立即执行,并且当前窗口也没有在做 View 的工作流工作的话就可以立即执行
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
}
//如果不需要立即执行,那就加到队列中执行。
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
void doDie() {
//对执行线程及窗口的一些状态做判断,校验和处理
checkThread();
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
//书中解释说窗口有子 view 就会调用该方法来销售 view
//这个 mAdded 我看了下会在 addView 的时候被设置为 true
//所以这个应该就是说真正的移除窗口
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
//......
}
}
mAdded = false;
}
//接着又调用了 WindowManagerGlobal 的方法
WindowManagerGlobal.getInstance().doRemoveView(this);
}
//WindowManagerGlobal
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
//移除对应的 ViewRootImpl 对象
mRoots.remove(index);
//移除对应的窗口属性
mParams.remove(index);
//移除对应的 View 对象
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
}
上面这个过程应该就是移除窗口的主逻辑了,至于 WMS 相关的部分应该在 dispatchDetachedFromWindow 里,
//ViewRootImpl
void dispatchDetachedFromWindow() {
//销毁和窗口关联的 Surface
destroySurface();
try {
//这里实际调用的是 Session 的 remove
mWindowSession.remove(mWindow);
}
//结束当前正在进行的或还没开始的 view 工作流工作
unscheduleTraversals();
}
//Session
public void remove(IWindow window) {
//看 WMS 去做的 removeWindow
mService.removeWindow(this, window);
}
//WMS
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
//很简单,逻辑就是找到代表窗口的 WindowState 对象,移除它
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return;
}
//销毁 WindowState 对象
win.removeIfPossible();
}
}
final WindowState windowForClientLocked(Session session, IWindow client, boolean throwOnError) {
return windowForClientLocked(session, client.asBinder(), throwOnError);
}
final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {
//就是从之前添加窗口时保存的 HashMap 集合中找到对应的 WindowState 对象
WindowState win = mWindowMap.get(client);
//做一些校验判断之后,返回有效的窗口信息
return win;
}
//WindowState
void removeIfPossible(){
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
//前面会做一些检查验证,看是否适合立即移除,如不适合就会延后
//立即移除
removeImmediately();
//通过 WMS 更新焦点窗口
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
}
void removeImmediately() {
super.removeImmediately();
//如果已经移除,就 return 不会重复做
if (mRemoved) {
return;
}
mRemoved = true;
//获取屏幕内容
final DisplayContent dc = getDisplayContent();
//通过 DisplayPolicy 进行窗口移除
//实际上就是将变量引用置为 null 值
dc.getDisplayPolicy().removeWindowLw(this);
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
//Session 做最后的窗口移除操作
//告知 session 对应的窗口数量要减一,当 session 关联的窗口数减为零时,session 将从 WMS 中移除
mSession.windowRemovedLocked();
//通过 WMS 做一些窗口移除的后续操作
//例如从 mWindowMap 中移除该窗口
mWmService.postWindowRemoveCleanupLocked(this);
}
上述移除窗口的过程简单来看就是从 WindowManagerGlobal 出发,经过 ViewRootImpl,再到 WMS,最后交由 WindowState 来做最后处理,WMS 做收尾工作。