WMS 分几步完成Z-Order的排序:
首先,建立窗口的时候为每个窗口分配BaseLayer 和SubLayer。
然后,按Z-Order的顺序将窗口加入到所在屏幕的窗口列表中。
最后,在显示的时候,动态计算窗口的Layer, 最终决定显示Z-Order。
1. BaseLayer 和SubLayer
WMS通过Layer来确定window的Z-Order,WMS为每个Window 分配一个Layer值,值越高,显示在越上面。
每个WindowState在创建的时候WMS时,都会分配一个baseLayer, 记录在WindowState.mBaseLayer中。子窗口还会对应有一个WindowState.mSubLayer值。
具体计算方法为:
主窗口:PhoneWindowManager.windowTypeToLayerLw(win.type)* TYPE_LAYER_MULTIPLIER(10000)+ TYPE_LAYER_OFFSET(1000)。所有相同类型的窗口具有相同的baseLayer值。例如:application类型的窗口对用的layer为2,那么BaseLayer值为2*10000+1000=21000。
子窗口:计算方法为PhoneWindowManager.subWindowTypeToLayerLw(a.type)。主窗口该值为0。
可以看到上述两个PhoneWindowManager函数都会要求win.type作为参数。函数都很简单,一看就明白, 不作具体说明。不同的窗口类型layer的定义参见@PhoneWindowManager.java 。下面是几个例子
static final int APPLICATION_LAYER = 2;
static final int PHONE_LAYER = 3;
static final int SEARCH_BAR_LAYER = 4;
static final int SYSTEM_DIALOG_LAYER = 5;
// toasts and the plugged-in battery thing
static final int TOAST_LAYER = 6;
对于子窗口类型layer定义如下,在Z-Order排序时正值的子窗口在主窗口上面,负值的子窗口在主窗口下面。
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
2. 按Z-Order的顺序将窗口加入到所在屏幕的窗口列表中
对于新加入App的窗口, WMS 会通过一定的顺序规则来添加窗口到相应的DisplayContent.mWindow中。前面说过DisplayContent.mWindow中的窗口是以Z-Orderp排序的。最后面的显示在最前面。
添加窗口到相应的DisplayContent.mWindow的函数为addWindowToListInOrderLocked@WindowManagerService.java。
举例说明, 假如要添加的Window是属于appToken2所代表的窗口。那么会按一下规则匹配添加窗口到相应位置:
A. 如果该窗口不是子窗口, 也就是主窗口或app窗口(例如dialog), 顺序匹配以下规则:
a. 如果已经有一个窗口在appToken2中, 顺序匹配一下规则:
1). type = TYPE_BASE_APPLICATION: 把这个窗口放在该app 的所有窗口的底部。使用函数:WindowManagerService. placeWindowBefore(pos, win)。
2). 如果最上层的窗口是 startingwindow: 把这个窗口放在starting窗口的下面,使用函数:indowManagerService.placeWindowBefore(token.windows.get(starting window), win)。
3). 如果最上面的窗口不是startingwindow, 将改窗口放在这个apptoken 的所有窗口的最上面。
b. 如果appToken2中没有任何窗口, 则按顺序匹配一下规则:
1). 找到放在该apptoken2紧上面的窗口,将新窗口放在该窗口下面。
2). 如果apptoken2上面没有任何窗口,则找到该apptoken2下的最上的那个窗口,放在该窗口的上面。
3).否则,按照窗口的mBaseLayer值确定该窗口位置。参见函数
addAppWindowToTokenListLocked()@WindowManagerService.java。
B. 如果是子窗口。 我们知道如果是子窗口,那么一定有主窗口存在。也就是app中一定会有窗口。这时匹配规则为:
a. sublayer < 0, 放在所有具有相同的sublayer的窗口的下面b. sublayer > 0, 放在所有具有相同sublayer的窗口的最上面。
参加函数addAttachedWindowToListLocked()@WindowManagerService.java。
这样,窗口以Z-order的顺序加入相应的display的窗口列表中(DisplayContent.mWindow)。
3.确定最终的窗口Layer。
经过上面两步后,WMS会根据DisplayContent.mWindow顺序, 对各个窗口的layer进行调整。 Android N 开始,WMS使用了一个新类WindowLayersController来管理调整Layer。具体调整函数为WindowLayersController. assignLayersLocked()。通过这个函数可以确定两个值,
窗口的layer:WindowState.mLayer
窗口动画的动态layer: WindowState.mWinAnimator.mAnimLayer
而最终显示的时候,使用的是窗口的动态layer, 也就是mAnimLayer, 它决定了窗口的最终的显示位置。
mAnimLayer通常为mLayer+播放的动画的layer调整值。关于播放动画的layer调整值计算参见AppWindowAnimator.setAnimation()。
具体调整函数就不讲了,通过以下命令我们可以看到调整后的结果:
adb shell dumpsys window -a
比如下面的一个按Z-Order排序的系统所有窗口,最前面的显示在最上面:
Window #13 Window{479e028 u0 NavigationBar}:
mBaseLayer=211000 mSubLayer=0 mAnimLayer=211000+0=211000 mLastLayer=211000
Window #12 Window{13975e u0 StatusBar}:
mBaseLayer=161000 mSubLayer=0 mAnimLayer=161000+0=161000 mLastLayer=161000
Window #11 Window{1e36363 u0 KeyguardScrim}:
mBaseLayer=141000 mSubLayer=0 mAnimLayer=141000+0=141000 mLastLayer=141000
Window #10Window{da1f22a u0 com.android.systemui.ImageWallpaper}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=41005+0=41005 mLastLayer=41005
Window #9 Window{dd2f356 u0 AssistPreviewPanel}:
mBaseLayer=41000 mSubLayer=0 mAnimLayer=41000+0=41000 mLastLayer=0
Window #8 Window{9359c00 u0 DockedStackDivider}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21030+0=21030 mLastLayer=0
Window #7 Window{4585746 u0 SurfaceView - InputMethod}:
mBaseLayer=121000 mSubLayer=1 mAnimLayer=21035+0=21035 mLastLayer=21050
Window #6 Window{42b459b u0 InputMethod}:
mBaseLayer=121000 mSubLayer=0 mAnimLayer=21030+0=21030 mLastLayer=21045
Window #5 Window{37a945 u0 com.android.settings/com.android.settings.Settings$WirelessSettingsActivity}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21025+0=21025 mLastLayer=21025
Window #4 Window{b61abcf u0 com.android.settings/com.android.settings.Settings}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21020+0=21020 mLastLayer=21020
Window #3 Window{d680e00 u0 com.touchtype.swiftkey/com.touchtype.onboarding.OnboardingBrandRecognition}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21015+0=21015 mLastLayer=22010
Window #2 Window{b93bcfb u0 com.android.home/com.android.home.HomeActivity}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21010+0=21010 mLastLayer=21010
Window #1 Window{b72704e u0 com.android.systemui/com.android.systemui.recents.RecentsActivity}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21005+0=21005 mLastLayer=22010
Window #0 Window{da1f22a u0 com.android.systemui.ImageWallpaper}:
mBaseLayer=21000 mSubLayer=0 mAnimLayer=21000+0=21000 mLastLayer=21000
那么在最终在surfaceFlinger中显示的时候, 会调用两个函数来设置窗口在surfaceFlinger中的Layer,这个layer也就是WindowStateAnimator.mAnimLayer:
mSurfaceController.setPositionAndLayer()@WindowStateAnimator.createSurfaceLocked()。mSurfaceController.prepareToShowInTransaction()@WindowStateAnimator.prepareSurfaceLocked()。
SurfaceFlinger会根据设定的layer大小确定显示的Z-Order, 也就是屏幕上窗口显示的顺序。