WindowManagerService(以下简称WMS)是Android Framework中一个重要的系统服务,用来管理系统中窗口(Window)的行为。Window是一个抽象的概念,它是一个矩形区域,用来绘制UI界面,响应用户的输入事件。Android系统的界面,从Framework层的角度来看,就是由一个一个窗口组合而成的。
在WMS中一个重要的功能就是对当前窗口的显示顺序进行排序。但是窗口只是一个抽象的概念,WMS中所做的事情实际上是根据各种条件计算窗口的显示层级,然后将这个表示层级的数值传给SurfaceFlinger,SurfaceFlinger根据这个层级信息去进行渲染。本文主要对Android 7.0版本中WMS关于计算窗口的显示层级的相关机制进行简单地分析。
一. 窗口的主序和次序
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
......
// 首先根据窗口类型判断这是不是一个子窗口
if ((mAttrs.type >= FIRST_SUB_WINDOW &&
mAttrs.type <= LAST_SUB_WINDOW)) {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
// 计算主序和子序,注意当前窗口是一个子窗口,其主序应该与其父窗口一致,所以传入的是其父窗口(attachWindow)的窗口类型
mBaseLayer = mPolicy.windowTypeToLayerLw(
attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
......
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
// 当前不是一个子窗口,非子窗口的mSubLayer都为0
mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = 0;
......
}
......
}
窗口的主序和次序都是通过WMS的策略类(PhoneWindowManager.java)获取的,其中的逻辑很简单,就是根据窗口的type返回一个2 ~ 31的数。根据上面的初始化过程我们可以看到,mBaseLayer的值会根据策略类返回的值再乘以10000并加上1000计算而成。根据源码中的注释:The multiplier here is to reserve space for multiple windows in the same type layer. 我们可以推测,这样计算的目的是为了区分同一类型的不同窗口的层级。也就是说,主序仅仅是按照窗口类型划分了一个显示顺序,但是同一类型的窗口不一定只有一个(计算同类型窗口的显示层级还需要其它的条件),不同类型窗口的主序相差10000是为了给这些同类型窗口的显示层级预留的空间。常见的窗口类型的主序如下:
窗口类型 | 主序 |
type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW(应用窗口) | 21000 |
TYPE_WALLPAPER(墙纸) | 21000 |
TYPE_PHONE(电话) | 31000 |
TYPE_TOAST(toast) | 81000 |
TYPE_INPUT_METHOD(输入法) | 101000 |
TYPE_STATUS_BAR(状态栏) | 161000 |
TYPE_KEYGUARD_DIALOG(锁屏) | 181000 |
子窗口类型 | 子序 |
TYPE_APPLICATION_MEDIA(SurfaceView) | -2 |
TYPE_APPLICATION_PANEL | 1 |
TYPE_APPLICATION_SUB_PANEL | 2 |
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
......
// 经过一些条件的检查,窗口的WindowState对象被创建,根据attrs.type决定其mBaseLayer以及mSubLayer的值
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
......
// 这里会对输入法窗口做特殊的处理,这里略去,通用的情况是调用此方法对新窗口位置进行插入
addWindowToListInOrderLocked(win, true);
}
addWindowToListInOrderLocked方法对新窗口进行插入的操作,其流程如上面流程图所示。下面对照代码分析一下具体流程:
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
" Callers=" + Debug.getCallers(4));
// 如果mAttachedWindow是空,则表示该窗口不是一个子窗口
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
// 注意这个变量,它表示该WindowState对象在所属同一WindowToken的所有WindowState中的位置
int tokenWindowsPos = 0;
if (token.appWindowToken != null) {
// appWindowToken不为空,意味着这是一个activity窗口
tokenWindowsPos = addAppWindowToListLocked(win);
} else {
addFreeWindowToListLocked(win);
}
if (addToToken) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + token);
// token.windows就是描述所属该token下的所有WindowState对象
// 比如一个activity弹出了一个AlertDialog窗口,这两个窗口的AppWindowToken是一个
token.windows.add(tokenWindowsPos, win);
}
} else {
// 这是一个子窗口
addAttachedWindowToListLocked(win, addToToken);
}
final AppWindowToken appToken = win.mAppToken;
if (appToken != null) {
if (addToToken) {
appToken.addWindow(win);
}
}
}
// 首先是addAppWindowToListLocked方法,该方法对activity窗口进行了插入
private int addAppWindowToListLocked(final WindowState win) {
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
// It doesn't matter this display is going away.
return 0;
}
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
final WindowList windows = displayContent.getWindowList();
// 获取同一DisplayContent中,所属该WindowToken的所有WindowState
WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
int tokenWindowsPos = 0;
// 如果存在相同的WindowToken的WindowState,则通过addAppWindowToTokenListLocked方法对其进行插入
// 比如这种情景就会走到这个case:从一个activity中弹出一个popup window
// 此时这个新的弹窗和activity的窗口属于同一WindowToken
if (!tokenWindowList.isEmpty()) {
// 这个方法下面会分析
return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList);
}
// No windows from this token on this display
if (localLOGV) Slog.v(TAG_WM, "Figuring out where to add app window " + client.asBinder()
+ " (token=" + token + ")");
// Figure out where the window should go, based on the
// order of applications.
// 这个是要插入位置(原来)的WindowState
WindowState pos = null;
// 遍历该DisplayContent中所有的Task
// 注意,流程走到这里意味这这个窗口是一个activity窗口,所以在此之前,
// AMS会通过addAppToken方法把其AppWindowToken添加到对应的task中去
// 此时正常的状态为:Task中有对应的AppWindowToken,
// 但是DisplayContent中的WindowList里面没有该WindowState
// 另外说明一点,通过addAppToken添加进来的AppWindowToken会记录在mTokenMaps中,
// 在addWindow的过程中,该AppWindowToken会通过mTokenMaps获取,
// 并赋值给WindowState的mToken成员(AppWindowToken是WindowToken的子类)
final ArrayList tasks = displayContent.getTasks();
int taskNdx;
int tokenNdx = -1;
for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
final AppWindowToken t = tokens.get(tokenNdx);
// 自顶向下遍历,正常情况下第一个就应该是目标的AppWindowToken,
// 此时会直接跳出循环,pos指向空
if (t == token) {
--tokenNdx;
if (tokenNdx < 0) {
// 如果tokenNdx小于0,意味着已经到该task的最后一个了,
// 再插入的位置应该是下一个task的顶部
--taskNdx;
if (taskNdx >= 0) {
tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1;
}
}
break;
}
// We haven't reached the token yet; if this token
// is not going to the bottom and has windows on this display, we can
// use it as an anchor for when we do reach the token.
// 如果第一个不是,那么pos就指向当前遍历到AppWindowToken所属的最下面一个
// 的WindowState(当前activity中最后一个window)
// 这样做的目的是这样,activity的WindowState的位置实际上要和activity的位置一致
// 如果下一轮迭代找到了对应的AppWindowToken,那么它应该被插入在它上面的AppWindowToken
// 的最后一个窗口的下面pos就指向了这个位置当前的WindowState,
// 后面新的WindowState就要被插入到这个位置
tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
if (!t.sendingToBottom && tokenWindowList.size() > 0) {
pos = tokenWindowList.get(0);
}
}
if (tokenNdx >= 0) {
// early exit
break;
}
}
// We now know the index into the apps. If we found
// an app window above, that gives us the position; else
// we need to look some more.
// 不为空的话则表示要插入的WindowState的AppWindowToken不在顶部
// 需要插入到它上面一个AppWindowToken所属的最下面的一个WindowState的后面
if (pos != null) {
// Move behind any windows attached to this one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
tokenWindowList =
getTokenWindowsOnDisplay(atoken, displayContent);
final int NC = tokenWindowList.size();
if (NC > 0) {
WindowState bottom = tokenWindowList.get(0);
if (bottom.mSubLayer < 0) {
// 确保pos指向的是上面AppWindowToken的最后一个WindowState
pos = bottom;
}
}
}
// 插入到DisplayContent所记录顺序的WindowList中
placeWindowBefore(pos, win);
// 返回该WindowState在其WindowToken中所有WindowState的位置
return tokenWindowsPos;
}
// Continue looking down until we find the first
// token that has windows on this display.
// 流程到这里,意味这要插入的WindowState所对应的activity是在top task的顶部(大部分情况是这种)
for ( ; taskNdx >= 0; --taskNdx) {
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
for ( ; tokenNdx >= 0; --tokenNdx) {
// 这里就是要看,到底要插入到谁的上面
// 理论上是插入在当前task的顶部WindowState的上面,但是有可能此时的activity是首次启动,
// task里面没有WindowState
// 这种情况下的状态为:Task里仅仅有准备添加的这个WindowState的AppWindowToken,
// 但是该AppWindowToken没有属于它的WindowState
//(注意新Window还未添加进去,此方法的返回值是新WindowState在AppWindowToken中的位置,
// 然后才会添加进去),所以此时pos仍然为空
// 但是当Task中有其他的AppWindowToken(app内部启动新的activity,并且不加NEW_TASK的flag)时,
// pos指向的是该Task内最上面的WindowState
final AppWindowToken t = tokens.get(tokenNdx);
tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
final int NW = tokenWindowList.size();
if (NW > 0) {
pos = tokenWindowList.get(NW-1);
break;
}
}
if (tokenNdx >= 0) {
// found
break;
}
}
// 如果pos不为空,则指向的是该Task最上面的WindowState,新的WindowState即将插入到它上面
if (pos != null) {
// Move in front of any windows attached to this
// one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
WindowState top = atoken.windows.get(NC-1);
if (top.mSubLayer >= 0) {
// 这里同上,确保pos是顶部的WindowState
pos = top;
}
}
}
placeWindowAfter(pos, win);
return tokenWindowsPos;
}
// Just search for the start of this layer.
// 走到这里,意味着该WindowState是新启动的一个activity的第一个窗口(新task的第一个WindowState)
// 由于没有可参考的activity窗口,所以需要通过mBaseLayer去插入
// 很明显,它将被插入到所有应用窗口的顶部
final int myLayer = win.mBaseLayer;
int i;
for (i = windows.size() - 1; i >= 0; --i) {
WindowState w = windows.get(i);
// Dock divider shares the base layer with application windows, but we want to always
// keep it above the application windows. The sharing of the base layer is intended
// for window animations, which need to be above the dock divider for the duration
// of the animation.
// 分屏的DOCK_DIVIDER(分屏模式下中间那个黑杆,mBaseLayer也是21000,但是要保证它在所有应用窗口的上面)
if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
break;
}
}
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+ windows.size());
windows.add(i + 1, win);
mWindowsChanged = true;
return tokenWindowsPos;
}
// 如果新添加的WindowState所对应的AppWindowToken已经有其它的WindowState存在了,
// 则需要通过addAppWindowToTokenListLocked方法插入
// 这里可以这样理解,要启动的窗口所属的activity之前已经存在了,
// activity的所有窗口(即我们上面一直说的所属同一AppWindowToken的WindowState)
// 顺序必然是连续的,所以此时新启动的WindowState必定要插入在这其中的某一个位置
// 注意,流程到这里是,已经判断过了该WindowState不是一个子窗口,所以不能用mSubLayer判断位置
private int addAppWindowToTokenListLocked(WindowState win, WindowToken token,
WindowList windows, WindowList tokenWindowList) {
int tokenWindowsPos;
// If this application has existing windows, we
// simply place the new window on top of them... but
// keep the starting window on top.
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// 如果新窗口是TYPE_BASE_APPLICATION类型,则需要插入在该AppWindowToken所有窗口的最底部
// Base windows go behind everything else.
WindowState lowestWindow = tokenWindowList.get(0);
placeWindowBefore(lowestWindow, win);
tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
} else {
AppWindowToken atoken = win.mAppToken;
final int windowListPos = tokenWindowList.size();
WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
if (atoken != null && lastWindow == atoken.startingWindow) {
// 如果存在starting window,则插入到starting window的下面
placeWindowBefore(lastWindow, win);
tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
} else {
// 否则,插入到该AppWindowToken所有窗口的最顶部
int newIdx = findIdxBasedOnAppTokens(win);
//there is a window above this one associated with the same
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+ windows.size());
windows.add(newIdx + 1, win);
if (newIdx < 0) {
// No window from token found on win's display.
tokenWindowsPos = 0;
} else {
tokenWindowsPos = indexOfWinInWindowList(
windows.get(newIdx), token.windows) + 1;
}
mWindowsChanged = true;
}
}
return tokenWindowsPos;
}
private void rebuildAppWindowListLocked(final DisplayContent displayContent) {
final WindowList windows = displayContent.getWindowList();
int NW = windows.size();
int i;
// 记录了自底向上第一个应用窗口下面的第一个窗口的位置
int lastBelow = -1;
int numRemoved = 0;
if (mRebuildTmp.length < NW) {
mRebuildTmp = new WindowState[NW+10];
}
// First remove all existing app windows.
// 遍历所有的WindowState,把应用窗口全部移除
i=0;
while (i < NW) {
WindowState w = windows.get(i);
if (w.mAppToken != null) {
WindowState win = windows.remove(i);
win.mRebuilding = true;
mRebuildTmp[numRemoved] = win;
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Rebuild removing window: " + win);
NW--;
numRemoved++;
continue;
} else if (lastBelow == i-1) {
// 根据PhoneWindowManager里面windowTypeToLayerLw方法我们可以知道,
// 应用窗口的主序是最低的
// 仅有墙纸窗口有可能在应用窗口下面
if (w.mAttrs.type == TYPE_WALLPAPER) {
lastBelow = i;
}
}
i++;
}
// Keep whatever windows were below the app windows still below,
// by skipping them.
lastBelow++;
i = lastBelow;
// First add all of the exiting app tokens... these are no longer
// in the main app list, but still have windows shown. We put them
// in the back because now that the animation is over we no longer
// will care about them.
// 首先把准备退出的窗口重新添加进来,这个mExitingAppTokens在removeAppToken时被记录
// 也就是说,即将退出的窗口都往下面放
final ArrayList stacks = displayContent.getStacks();
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
AppTokenList exitingAppTokens = stacks.get(stackNdx).mExitingAppTokens;
int NT = exitingAppTokens.size();
for (int j = 0; j < NT; j++) {
i = reAddAppWindowsLocked(displayContent, i, exitingAppTokens.get(j));
}
}
// And add in the still active app tokens in Z order.
// 接着把非退出的窗口按照Z order重新添加回去,实际上有就是根据activity的顺序添加
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ArrayList tasks = stacks.get(stackNdx).getTasks();
final int numTasks = tasks.size();
for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
if (wtoken.mIsExiting && !wtoken.waitingForReplacement()) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
}
}
}
// 输出一些日志信息
......
}
private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index,
WindowToken token) {
final int NW = token.windows.size();
// 由于每次传入的都是一个WindowToken,所以这个方法需要把该WindowToken下的所有窗口都插入回去
for (int i=0; i= 0) {
// 此时cwin就是父窗口,也就是传进来的参数win
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding child window at "
+ index + ": " + cwin);
win.mRebuilding = false;
// 把父窗口插入回去
windows.add(index, win);
index++;
winAdded = true;
}
// 按原来的顺序继续插入回去
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
+ index + ": " + cwin);
cwin.mRebuilding = false;
windows.add(index, cwin);
index++;
}
// winAdded表示父窗口是否被插入回去,如果为false,
// 则要把父窗口插入到其它子窗口的上面(理论上不应该出现这种情况)
if (!winAdded) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
+ index + ": " + win);
win.mRebuilding = false;
windows.add(index, win);
index++;
}
mWindowsChanged = true;
return index;
}
// 这里传入的就是DisplayContent的WindowList
final void assignLayersLocked(WindowList windows) {
if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
new RuntimeException("here").fillInStackTrace());
clear();
int curBaseLayer = 0;
int curLayer = 0;
boolean anyLayerChanged = false;
for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
final WindowState w = windows.get(i);
boolean layerChanged = false;
int oldLayer = w.mLayer;
// 自底向上遍历,第一个窗口curLayer和mBaseLayer相等
// 每往上迭代一次,遍历到的窗口的curLayer就添加数值为5的偏移量
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
}
// 更新该窗口的mAnimLayer,也就是动画层级(可以理解为动画显示时,该窗口的层级)
assignAnimLayer(w, curLayer);
// TODO: Preserved old behavior of code here but not sure comparing
// oldLayer to mAnimLayer and mLayer makes sense...though the
// worst case would be unintentionalp layer reassignment.
if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
}
// 记录当前应用窗口的最高显示层级
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
w.mWinAnimator.mAnimLayer);
}
// 记录一些特殊窗口,比如分屏相关的窗口,它们的显示层级有特殊要求
collectSpecialWindows(w);
// 和Dim Layer有关,窗口显示层级发生变化时,需要对Dim Layer进行调度
if (layerChanged) {
w.scheduleAnimationIfDimming();
}
}
// 遍历结束,调整特殊窗口的层级
adjustSpecialWindows();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && anyLayerChanged
&& windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowLayersChangedLocked();
}
if (DEBUG_LAYERS) logDebugLayers(windows);
}
// 这是一个关键的方法,用于更新窗口动画层级
private void assignAnimLayer(WindowState w, int layer) {
// 传入的layer就是刚才计算的curLayer,也就是基于mBaseLayer以偏移量5递增的数值
// 如果不受动画参数影响(w.getAnimLayerAdjustment()),
// 并且它不是特殊窗口(getSpecialWindowAnimLayerAdjustment(w),输入法和墙纸)
// 那么窗口的显示层级和窗口的顺序是一致的(相邻窗口的mLayer差5)
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
getSpecialWindowAnimLayerAdjustment(w);
if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
&& w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
// 这个是用于过渡动画的,因为某些特殊类型的过渡动画会在动画过程中创建一块新的Surface用于配合动画
// 但是这个Surface没有在WMS中以Window的形式体现,
// 其显示属性都要有AppWindowAnimator自己控制(直接对Surface操作)
// 这里就是更新了这种Surface的层级属性
w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
}
}
public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
int stackClip) {
......
// 这里会获得通过Animaion.java的setZAdjustment设置的zorder类型
int zorder = anim.getZAdjustment();
int adj = 0;
// ZORDER_TOP则会+1000,ZORDER_BOTTOM则会-1000
if (zorder == Animation.ZORDER_TOP) {
adj = TYPE_LAYER_OFFSET;
} else if (zorder == Animation.ZORDER_BOTTOM) {
adj = -TYPE_LAYER_OFFSET;
}
if (animLayerAdjustment != adj) {
// 上面getAnimLayerAdjustment获得的就是这个值
animLayerAdjustment = adj;
// 这里会通知一些特殊的地方,动画层级改变:比如墙纸
updateLayers();
}
......
}
private void adjustSpecialWindows() {
// 这个初始化的值表示了当前最高的应用窗口层级+5
int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
// For pinned and docked stack window, we want to make them above other windows also when
// these windows are animating.
// Docked Stack(分屏模式下的上半屏窗口)中的窗口需要现实在所有应用窗口之上,这里层级被重新调整了
while (!mDockedWindows.isEmpty()) {
// assignAndIncreaseLayerIfNeeded方法会通过assignAnimLayer更新该window的mLayer以及mAnimLayer
// 更新完后,layer会再次+5
layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
}
// 接下来更新分屏杆的窗口,它需要在所有应用窗口之上,所以需要先更新Docked Stack里面的应用窗口
// 在此方法中,越靠后更新的窗口,layer越高
layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
// 调整输入法与分屏杆的层级关系
if (mDockDivider != null && mDockDivider.isVisibleLw()) {
while (!mInputMethodWindows.isEmpty()) {
final WindowState w = mInputMethodWindows.remove();
// Only ever move IME windows up, else we brake IME for windows above the divider.
if (layer > w.mLayer) {
layer = assignAndIncreaseLayerIfNeeded(w, layer);
}
}
}
// We know that we will be animating a relaunching window in the near future, which will
// receive a z-order increase. We want the replaced window to immediately receive the same
// treatment, e.g. to be above the dock divider.
while (!mReplacingWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
}
// 这是一种特殊模式的窗口,类似于画中画模式
while (!mPinnedWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
}
}