android view 窗口的Z次序管理

在Android系统中,窗口是有分组概念的,例如,Activity中弹出的所有PopupWindow会随着Activity的隐藏而隐藏,可以说这些都附属于Actvity的子窗口分组,对于Dialog也同样如此,只不过Dialog与Activity属于同一个分组。之间已经简单介绍了窗口类型划分:应用窗口、子窗口、系统窗口,Activity与Dialog都属于应用窗口,而PopupWindow属于子窗口,Toast、输入法等属于系统窗口。只有应用窗口与系统窗口可以作为父窗口,子窗口不能作为子窗口的父窗口,也就说Activity与Dialog或者系统窗口中可以弹出PopupWindow,但是PopupWindow不能在自己内部弹出PopupWindow子窗口。日常开发中,一些常见的问题都同窗口的分组有关系,比如为什么新建Dialog的时候必须要用Activity的Context,而不能用Application的;为什么不能以PopupWindow的View为锚点弹出子PopupWindow?其实这里面就牵扯都Android的窗口组织管理形式,本文主要包含以下几点内容:

  • 窗口的分组管理 :应用窗口组、子窗口组、系统窗口组
  • Activity、Dialg应用窗口及PopWindow子窗口的添加原理跟注意事项
  • 窗口的Z次序管理:窗口的分配序号、次序调整等
  • WMS中窗口次序分配如何影响SurfaceFlinger服务

窗口的Z次序管理:窗口的分配序号、次序调整等
虽然我们看到的手机屏幕只是一个二维平面X*Y,但其实Android系统是有隐形的Z坐标轴的,其方向与手机屏幕垂直,与我们的实现平行,所以并不能感知到。

前面分析了窗口分组的时候涉及了两个对象WindowState与Windtoken,但仅限分组,分组无法决定窗口的显示的Z-order,那么再WMS是怎么管理所有窗口的Z-order的? 在WMS中窗口被抽象成WindowState,因此WindowState内部一定有属性来标志这个窗口的Z-order,实现也确实如此,WindowState采用三个个int值mBaseLayer+ mSubLayer + mLayer 来标志窗口所处的位置,前两个主要是根据窗口类型确定窗口位置,mLayer才是真正的值,定义如下:

final class WindowState implements WindowManagerPolicy.WindowState {

    final WindowList mChildWindows = new WindowList();
    final int mBaseLayer;
    final int mSubLayer;
     <!--最终Z次序的赋值-->
   int mLayer;

    }

从名字很容知道mBaseLayer是标志窗口的主次序,面向的是一个窗口组,而mSubLayer主要面向单独窗口,要来标志一个窗口在一组窗口中的位置,对两者来说值越大,窗口越靠前,从此final属性知道,两者的值是不能修改的,而mLayer可以修改,对于系统窗口,一般不会同时显示两个,因此,可以用主序决定,比较特殊的就是Activity与子窗口,首先子窗口的主序肯定是父窗口决定的,子窗口只关心次序就行。而父窗口的主序却相对麻烦,比如对于应用窗口来说,他们的主序都是一样的,因此还要有一个其他的维度来作为参考,比如对于Activity,主序都是一样的,怎么定他们真正的Z-order呢?其实Activity的顺序是由AMS保证的,这个顺序定了,WMS端Activity窗口的顺序也是定了,这样下来次序也方便定了。

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, final DisplayContent displayContent) {
        ...
            <!--关键点1  子窗口类型的Z order-->
        if ((mAttrs.type >= FIRST_SUB_WINDOW &&
                mAttrs.type <= LAST_SUB_WINDOW)) {
            mBaseLayer = mPolicy.windowTypeToLayerLw(
                    attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;
            mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
            mAttachedWindow = attachedWindow;               final WindowList childWindows = mAttachedWindow.mChildWindows;
            final int numChildWindows = childWindows.size();
            if (numChildWindows == 0) {
                childWindows.add(this);
            } else {
             ...
        } else {
            <!--关键点2  普通窗口类型的Z order-->
            mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
                    * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;
            mSubLayer = 0;
            mAttachedWindow = null;
            mLayoutAttached = false;
        }
       ...
    }

由于窗口所能选择的类型是确定的,因此mBaseLayer与mSubLayer所能选择的值只有固定几个,很明显这两个参数不能精确的确定Z-order,还会有其他微调的手段,也仅限微调,在系统层面,决定了不同类型窗口所处的位置,比如系统Toast类型的窗口一定处于所有应用窗口之上,不过我们最关心的是Activity类的窗口如何确定Z-order的,在new WindowState之后,只是粗略的确定了Activity窗口的次序,看一下添加窗口的示意代码:

addWindow(){
    <!--1-->
    new WindowState
    <!--2-->
    addWindowToListInOrderLocked(win, true);
    <!--3-->
    assignLayersLocked(displayContent.getWindowList());
    }
新建state对象之后,Z-order还要通过addWindowToListInOrderLocked及assignLayersLocked才能确定,addWindowToListInOrderLocked主要是根据窗口的Token找到归属,插入到对应Token的WindowState列表,如果是子窗口还要插入到父窗口的对应位置中:

插入到特定位置后其实Z-order就确定了,接下来就是通过assignLayersLocked为WindowState分配真正的Z-order mLayer,

   private final void assignLayersLocked(WindowList windows) {
        int N = windows.size();
        int curBaseLayer = 0;
        int curLayer = 0;
        int i;

        boolean anyLayerChanged = false;
            for (i=0; i<N; i++) {
            final WindowState w = windows.get(i);
            final WindowStateAnimator winAnimator = w.mWinAnimator;
            boolean layerChanged = false;
            int oldLayer = w.mLayer;
            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
                    || (i > 0 && w.mIsWallpaper)) {
                <!--通过偏移量-->
                curLayer += WINDOW_LAYER_MULTIPLIER;
                w.mLayer = curLayer;
            } else {
                curBaseLayer = curLayer = w.mBaseLayer;
                w.mLayer = curLayer;
            }
            if (w.mLayer != oldLayer) {
                layerChanged = true;
                anyLayerChanged = true;
            }
            ...
    }

mLayer最终确定后,窗口的次序也就确定了,这个顺序要最终通过后续的relayout更新到SurfaceFlinger服务,之后,SurfaceFlinger在图层混排的时候才知道如何处理。

WMS中窗口次序分配如何影响SurfaceFlinger服务
SurfaceFlinger在图层混排的时候应该不会混排所有的窗口,只会混排可见的窗口,比如有多个全屏Activity的时候,SurfaceFlinger只会处理最上面的,那么SurfaceFlinger如何知道哪些窗口可见哪些不可见呢?前文分析了WMS分配Z-order之后,要通过setLayer更新到SurfaceFlinger,接下来看具体流程,创建SurfaceControl之后,会创建一次事务,确定Surface的次序:

   SurfaceControl.openTransaction();
            try {
                mSurfaceX = left;
                mSurfaceY = top;
                    try {
                    mSurfaceControl.setPosition(left, top);
                    mSurfaceLayer = mAnimLayer;
                    final DisplayContent displayContent = w.getDisplayContent();
                    if (displayContent != null) {
                        mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
                    }
                    <!--设置次序-->
                    mSurfaceControl.setLayer(mAnimLayer);
                    mSurfaceControl.setAlpha(0);
                    mSurfaceShown = false;
                } catch (RuntimeException e) {
                    mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
                }
                mLastHidden = true;
            } finally {
                SurfaceControl.closeTransaction();
            }
        }

这里通过openTransaction与closeTransaction保证一次事务的完整性,中间就Surface次序的调整,closeTransaction会与SurfaceFlinger通信,通知SurfaceFlinger更新Surface信息,这其中就包括Z-order。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值