Android 窗口管理:Z-Order管理

        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, 也就是屏幕上窗口显示的顺序。
    原文作者:april_12345
    原文地址: https://blog.csdn.net/april_12345/article/details/52933316
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞