1,概述
在Android世界中,window处在三维世界中,即xyz坐标系。我们所说的z坐标系,简称z序,决定多个窗口在屏幕显示的次序。z序越大,就可以覆盖z序比它小的窗口,符合物理规律。
通过xml创建的窗口,如果不调用系统函数设置,其window.type默认为TYPE_APPLICATION,
在此先贴TYPE类型源码,其值递增;
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = TYPE_BASE_APPLICATION,
to = "BASE_APPLICATION"),
// 应用普通窗口
@ViewDebug.IntToString(from = TYPE_APPLICATION,
to = "APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING,
to = "APPLICATION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DRAWN_APPLICATION,
to = "DRAWN_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL,
to = "APPLICATION_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA,
to = "APPLICATION_MEDIA"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL,
to = "APPLICATION_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_ABOVE_SUB_PANEL,
to = "APPLICATION_ABOVE_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG,
to = "APPLICATION_ATTACHED_DIALOG"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY,
to = "APPLICATION_MEDIA_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR,
to = "STATUS_BAR"),
@ViewDebug.IntToString(from = TYPE_SEARCH_BAR,
to = "SEARCH_BAR"),
@ViewDebug.IntToString(from = TYPE_PHONE,
to = "PHONE"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT,
to = "SYSTEM_ALERT"),
@ViewDebug.IntToString(from = TYPE_TOAST,
to = "TOAST"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY,
to = "SYSTEM_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_PRIORITY_PHONE,
to = "PRIORITY_PHONE"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_DIALOG,
to = "SYSTEM_DIALOG"),
@ViewDebug.IntToString(from = TYPE_KEYGUARD_DIALOG,
to = "KEYGUARD_DIALOG"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR,
to = "SYSTEM_ERROR"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD,
to = "INPUT_METHOD"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG,
to = "INPUT_METHOD_DIALOG"),
@ViewDebug.IntToString(from = TYPE_WALLPAPER,
to = "WALLPAPER"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR_PANEL,
to = "STATUS_BAR_PANEL"),
@ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY,
to = "SECURE_SYSTEM_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_DRAG,
to = "DRAG"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR_SUB_PANEL,
to = "STATUS_BAR_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_POINTER,
to = "POINTER"),
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR,
to = "NAVIGATION_BAR"),
@ViewDebug.IntToString(from = TYPE_VOLUME_OVERLAY,
to = "VOLUME_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS,
to = "BOOT_PROGRESS"),
@ViewDebug.IntToString(from = TYPE_INPUT_CONSUMER,
to = "INPUT_CONSUMER"),
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL,
to = "NAVIGATION_BAR_PANEL"),
@ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY,
to = "DISPLAY_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY,
to = "MAGNIFICATION_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_PRESENTATION,
to = "PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION,
to = "PRIVATE_PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION,
to = "VOICE_INTERACTION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING,
to = "VOICE_INTERACTION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER,
to = "DOCK_DIVIDER"),
@ViewDebug.IntToString(from = TYPE_QS_DIALOG,
to = "QS_DIALOG"),
@ViewDebug.IntToString(from = TYPE_SCREENSHOT,
to = "SCREENSHOT"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
to = "APPLICATION_OVERLAY")
})
@WindowType
public int type;
ok,知道这些基本type后,咱们进入WMS.addWindow()查看具体排序逻辑;
2,addWindow()
在此解释下,addWindow是在创建MainActivity时wms调用的函数,而addView是应用单独拥有的wm添加子窗口的函数,后者添加窗口依据为窗口子序;
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
//省略参数校验代码
//......
//为新窗口创建一个windowState,
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
ProtoLog.w(WM_ERROR, "Adding window client %s"
+ " that is dead, aborting.", client.asBinder());
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {
ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
// If adding a toast requires a token for this app we always schedule hiding
// toast windows to make sure they don't stick around longer then necessary.
// We hide instead of remove such windows as apps aren't prepared to handle
// windows being removed under them.
//
// If the app is older it can add toasts without a token and hence overlay
// other apps. To be maximally compatible with these apps we will hide the
// window after the toast timeout only if the focused window is from another
// UID, otherwise we allow unlimited duration. When a UID looses focus we
// schedule hiding all of its toast windows.
if (type == TYPE_TOAST) {
if (!displayContent.canAddToastWindowForUid(callingUid)) {
ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// Make sure this happens before we moved focus as one can make the
// toast focusable to force it not being hidden after the timeout.
// Focusable toasts are always timed out to prevent a focused app to
// show a focusable toasts while it has focus which will be kept on
// the screen after the activity goes away.
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
|| displayContent.mCurrentFocus == null
|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
if (mUseBLAST) {
res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
}
if (sEnableTripleBuffering) {
res |= WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING;
}
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
}
if (excludeWindowTypeFromTapOutTask(type)) {
displayContent.mTapExcludedWindows.add(win);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
UserHandle.getUserId(win.getOwningUid()));
win.setHiddenWhileSuspended(suspended);
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
final ActivityRecord tokenActivity = token.asActivityRecord();
if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
tokenActivity.startingWindow = win;
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
activity, win);
}
boolean imMayMove = true;
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
if (type == TYPE_INPUT_METHOD) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
if (type == TYPE_WALLPAPER) {
displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
// This is to avoid a new window being placed between the
// wallpaper and its target.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
if (activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
// the app is relaunching.
prepareNoneTransitionForRelaunching(activity);
}
if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outContentInsets,
outStableInsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
}
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
win.getParent().assignChildLayers();
if (focusChanged) {
displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
getInsetsSourceControls(win, outActiveControls);
}
Binder.restoreCallingIdentity(origId);
return res;
}
addWindow函数比较长,但结构清晰,先检查参数合法性,然后跟进type解析逻辑,创建一个WindowState,用来保存window状态,windowState构造方法中,为普通窗口赋值z序,mPolicy是WindowManagerPolicy,
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
//主序
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//子序,默认0,即和父窗口平级
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;