大家都知道WMS是很重要的一个服务,它主要的功能有:
- 窗口管理:负责添加、启动和删除窗口,窗口的大小,层级也是WMS管理
- 窗口动画:窗口切换动画管理,管理类为WindowAnimator
- 输入系统的中转站:InputManagerService对触摸事件进行处理,WMS作为中转站,寻找合适的窗口来处理反馈信息。
- Surface管理:每个窗口有一个Surface,Surface来绘制。
WMS的创建
WMS是在SystemServer进程中被创建的,SystemServer的main方法中。
在SystemServer的main方法中,系统分别调用了startBootstrapServices、startCoreServices、startOtherServices三个方法来分别启动引导服务、核心服务、其他服务。
WMS就是在startOtherServices方法中启动的。
在startOtherServices方法中会先获取Watchdog实例,Watchdog是用来监控关键服务的运行状况。然后会创建IMS(InputManagerService)。接着通过WindowManagerService的main方法获取到WMS的实例,该方法会把IMS传入。然后将IMS,WMS都注册到ServiceManager中。最后调用WMS的displayReady方法来初始化屏幕显示信息,调用WMS的systemReady方法来通知WMS系统的初始化工作已经完成了。
下面看看WMS的构造方法:
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
...
mInputManager = inputManager; // Must be before createDisplayContentLocked.
...
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
createDisplayContentLocked(display);
}
...
mActivityManager = ActivityManager.getService();
...
mAnimator = new WindowAnimator(this);
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}
代码经过了简化,我们可以看到,在WMS的构造方法中,先对inputManager进行了赋值。
然后获取到了DisplayManager,通过getDisplays方法获取Display数组,按理来说每个显示设备都会有一个Display实例。
通过遍历将display通过createDisplayContentLocked方法封装成DisplayContent,并设置在WMS中,DisplayContent用来描述一块屏幕。
然后会获取AMS实例,创建WindowAnimator来管理窗口动画。
接着初始化窗口管理策略的接口类WindowManagerPolicy,这个用来定义一个窗口策略要遵循的规范。然后通过addMonitor将WMS添加到Watchdog里面。Watchdog每分钟会对系统服务进行检查,如果出现死锁,就会杀死Watchdog所在的进程,就是SystemServer进程。
WMS的重要成员
了解WMS首先需要链接WMS中重要的成员变量:
//WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略的通用规范,
//并提供WindowManager所有的特定UI行为。具体实现类是PhoneWindowManager,
//这个实现类在WMS创建时被创建。
final WindowManagerPolicy mPolicy;
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
//Session前面提到过,用于进程间通信,其他应用程序想要和WMS通信就需要通过Session,
//每个应用程序进程都会有一个Session。WMS保存Session用来记录所有向WMS提出窗口管理服务的客户端
final ArraySet<Session> mSessions = new ArraySet<>();
//WindowHashMap的value限定了是WindowState,WindowState保存的是窗口的信息,
//是用来描述一个窗口的,所以mWindowMap中保存了所有窗口的信息
final WindowHashMap mWindowMap = new WindowHashMap();
//WindowToken有两个作用,一个是当作窗口令牌,当应用程序想要像WMS申请创建一个窗口,
//需要向WMS出示有效的WindowToken。AppWindowTOken是WindowToken的子类,用来描述应用程序的
//WindowToken结构,应用程序中每个Activity都对应一个AppWindowToken。
//WindowToken还会将同一个Acticity的窗口合集在一起,方便管理。
//mFinishedStarting就是用来储存已经启动完成的Acticity的AppWindowToken。
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
//用来储存已经绘制完成,但是不需要展示任何保存的surface的界面的AppWindowToken
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
//保存正在调整大小的窗口的信息。
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
//用于管理窗口动画
final WindowAnimator mAnimator;
//Handler类,用户将任务加入到主线程消息队列
final H mH = new H();
//输入系统的管理者,会对触摸事件进行管理。
final InputManagerService mInputManager;
Window的添加过程
无论是Acticity还是系统窗口,他们添加过程都会调用WMS的addWindow方法,下面我们讲解这个过程
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];
//这里最终调用的是PhoneWindowManager的checkAddPermission方法,
//根据传入的attrs中的type来判断是否可以添加窗口,如果不行,直接返回ADD_INVALID_DISPLAY状态
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
//根据displayId获取窗口要添加到哪个DisplayContent,
//如果没有找到返回ADD_INVALID_DISPLAY状态。
//DisplayContent用来描述一块屏幕
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
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;
}
...
//这里判断type是否是大于1000小于1999,如果是,说明是一个子窗口,类似PopupWindow这种
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//通过windowForClientLocked方法,传入token,token是IBinder类对象,
//方法内部会把token作为key,从mWindowMap中查找该子窗口的父窗口
parentWindow = windowForClientLocked(null, attrs.token, false);
//对父窗口进行判断,如果父窗口为null或者父窗口type不正确,都会返回错误的状态。
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;
//通过getWindowToken方法获取WindoToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
//根据是否有父窗口,获取正确的type
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
//如果token为null,进行其他判断
if (token == null) {
//如果添加的type为应用程序窗口,token不能为null,这里直接返回错误状态
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
//进过一系列的判断,会在这里创建WindowToken,这里传入的第四个参数为false,代表这里是
//隐式创建的
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//如果token不为null,创建的是应用程序窗口,直接获取AppWindowToken
atoken = token.asAppWindowToken();
//根据atoken是否为null返回不同的返回值。
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.asAppWindowToken() != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
//如果上面的的判断没有return,会在这里创建一个WindowState,this为当前WMS,
//session是用来和WMS通信的,client是IWindow对象,
//IWindow会将WMS操作回调给ViewRootImpl,token是WindowToken。
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
//判断添加窗口的客户端是否还存在
if (win.mDeathRecipient == null) {
// 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;
}
//判断窗口的DisplayContent是否还存在
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//调用PhoneWindowManager的adjustWindowParamsLw,
//在方法中会根据窗口的type对LayoutParamas进行一些修改
//这里传入的win.mAttrs其实就是WindowManager.LayoutParams
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
//调用prepareAddWindowLw准备将窗口添加到系统中。如果添加的是应用程序窗口
//这里不会做额外操作,会回直接返回ADD_OKAY状态
res = mPolicy.prepareAddWindowLw(win, attrs);
...
win.attach();
//将WindowState添加到WindowHashMap中,key就是将IWindow转换成Binder
mWindowMap.put(client.asBinder(), win);
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 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;
//将WindowState添加到对应的WindowToken中
win.mToken.addWindow(win);
...
boolean focusChanged = false;
if (win.canReceiveKeys()) {
//更新焦点窗口
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
...
//window已经展示,如果方向有变动,更新相应参数
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
reportNewConfig = true;
}
return res;
}
以上就是添加窗口的过程,东西很多,大家慢慢看。
Window删除过程
window的删除首先会调用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);
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);
}
}
这里会对view进行一些判断,然后调用removeViewLocked方法:
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
根据传入的index获取ViewRootImpl,获取到对应的View。然后获取到InputMethodManager,并调用windowDismissed结束触摸相关的逻辑。然后调用ViewRootImpl的die方法。
下面来看看die方法:
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
doDie();
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;
}
这里我们可以看到,如果immediate为true,并且mIsInTraversal为false,就会马上执行doDie(),并且不会执行下面的代码。mIsInTraversal在执行ViewRootImpl的performTraversals方法的时候设置为true,方法执行完成后设置为false。意思就是如果此时没有执行performTraversals,就可以立即执行doDie方法。
下面来看看doDie方法:
void doDie() {
//先检查当前线程是否为主线程
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
//如果已经调用过了该方法,这里会返回,如果没有调用,会将mRemoved设置为true。
if (mRemoved) {
return;
}
mRemoved = true;
//如果有子View,回调用dispatchDetachedFromWindow方法来销毁View
if (mAdded) {
dispatchDetachedFromWindow();
}
...
mAdded = false;
}
//最后调用doRemove方法
WindowManagerGlobal.getInstance().doRemoveView(this);
}
下面看看doRemoveView方法的代码:
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
删除View相关的信息
下面我们再来看看doDie方法中的dispatchDetachedFromWindow方法:
void dispatchDetachedFromWindow() {
...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
...
}
我们可以看到这里调用了mWindowSession的remove方法。mWindowSession是IWindowSession,IWindowSession在服务端是Session实现的,下面看看Session的remove方法:
mService.removeWindow(this,window);
这里的mService是WMS,我们看看WMS中的removeWindow方法:
void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return;
}
win.removeIfPossible();
}
}
现获取WindowState,WindowState用来保存窗口信息,然后调用WindowState的removeIfPossible方法
@Override
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
...
removeImmediately();
// 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);
}
先进性一些列判断,如果为true就会先延迟删除View。如果没有,这里会执行removeImmediately方法。
@Override
void removeImmediately() {
super.removeImmediately();
//防止重复调用
if (mRemoved) {
// Nothing to do.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"WS.removeImmediately: " + this + " Already removed...");
return;
}
mRemoved = true;
mWillReplaceWindow = false;
if (mReplacementWindow != null) {
mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
}
final DisplayContent dc = getDisplayContent();
if (mService.mInputMethodTarget == this) {
dc.computeImeTarget(true /* updateImeTarget */);
}
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
dc.mTapExcludedWindows.remove(this);
}
mPolicy.removeWindowLw(this);
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
mSession.windowRemovedLocked();
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);
}
该方法是对View相关的一些信息进行处理,最后调用postWindowRemoveCleanupLocked也是方便View清理。到这里大致的删除Window流程就完成了。