当前位置: 首页 > 工具软件 > StackBox > 使用案例 >

DisplayContent、StackBox、TaskStack笔记

华景明
2023-12-01

文章仅零散记录自己的一点理解,仅供自己参考。

每个显示设备,都有一个Display对象,DisplayManagerService专门管理这些Display。

1、DisplayContent()

  1. <span style="font-size:18px;">    DisplayContent(Display display, WindowManagerService service) { 
  2.         mDisplay = display; 
  3.         mDisplayId = display.getDisplayId(); 
  4.         display.getDisplayInfo(mDisplayInfo); 
  5.         isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; 
  6.         mService = service; 
  7.  
  8.         StackBox newBox = new StackBox(service,this, null); 
  9.         mStackBoxes.add(newBox); 
  10.         TaskStack newStack = new TaskStack(service, HOME_STACK_ID,this); 
  11.         newStack.mStackBox = newBox; 
  12.         newBox.mStack = newStack; 
  13.         mHomeStack = newStack; 
  14.     }</span> 
<span style="font-size:18px;">    DisplayContent(Display display, WindowManagerService service) {
        mDisplay = display;
        mDisplayId = display.getDisplayId();
        display.getDisplayInfo(mDisplayInfo);
        isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
        mService = service;

        StackBox newBox = new StackBox(service, this, null);
        mStackBoxes.add(newBox);
        TaskStack newStack = new TaskStack(service, HOME_STACK_ID, this);
        newStack.mStackBox = newBox;
        newBox.mStack = newStack;
        mHomeStack = newStack;
    }</span>

对应在WMS中,每个Display对象都会给他new一个DisplayContent,保存跟这个Display相关的窗口等信息,这点从WMS的构造函数可以看出来。从DisplayContent()构造函数中还可以看出,每一个DisplayContent至少包含一个StackBox和TaskStack

createDisplayContentLocked()-->getDisplayContentLocked()-->WMS.newDisplayContentLocked()-->new DisplayContent();

  1. <span style="font-size:18px;">WindowManagerService(){ 
  2.          ............  
  3.         Display[] displays = mDisplayManager.getDisplays(); 
  4.         for (Display display : displays) { 
  5.             createDisplayContentLocked(display); 
  6.         } 
  7.         ........ 
  8. }</span> 
<span style="font-size:18px;">WindowManagerService(){
         ............ 
        Display[] displays = mDisplayManager.getDisplays();
        for (Display display : displays) {
            createDisplayContentLocked(display);
        }
        ........
}</span>

2、mInitialDisplayWidth、mInitialDisplayHeight、mInitialDisplayDensity

保存的是初始屏幕宽度、高度、密度

3、mDisplayInfo

由第1条中的DisplayContent()构造函数中可以看出,mDisplayInfo就是从Display中获取的,保存着Display的相关信息。

4、layoutNeeded

当有窗口需要Layout时,layoutNeeded就会被设为true。

5、mStackBoxes

正常来说mStackBoxes中会保存两个StackBox,一个StackBox(0)里面只包含Launcher,另一个StackBox包含所有其他窗口。

①StackBox.mParent

  1. <span style="font-size:18px;">   /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
  2.      * is this entire size of mDisplayContent. */</span> 
<span style="font-size:18px;">    /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
     * is this entire size of mDisplayContent. */</span>
mParent表示由哪个StackBox分裂而来,但是对于StackBox 0和StackBox 1的mParent都为null。

②StackBox.mBounds

WMS.performLayoutLockedInner()-->DisplayContent.setStackBoxSize()-->StackBox.setStackBoxSizes()-->mBounds.set(bounds);

bounds尺寸来源于mPolicy.getContentRectLw(bounds);

  1. <span style="font-size:18px;">   public void getContentRectLw(Rect r) { 
  2.         r.set(mContentLeft, mContentTop, mContentRight, mContentBottom); 
  3.     }</span> 
<span style="font-size:18px;">    public void getContentRectLw(Rect r) {
        r.set(mContentLeft, mContentTop, mContentRight, mContentBottom);
    }</span>
对于720*1280尺寸的手机, (mContentLeft, mContentTop, mContentRight, mContentBottom)=(0,50,720,1280),踢出了状态栏高度,因此mBounds=(0,50,720,1280)
StackBox.mVertical

这个变量表示mFirst 和mSecond分裂方向是否是垂直分裂还是左右分裂,具体见split()函数。

/** Relative orientation of mFirst and mSecond. */

④StackBox.layoutNeeded

⑤StackBox.

⑥StackBox.mStack

/** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */

6、StackBox.split()

StackBox分裂函数,分裂的两个StackBox分别保存在mFirst和mSecond中(二叉树方式分裂)。

  1. <span style="font-size:18px;">    TaskStack split(int stackId,int relativeStackBoxId, int position,float weight) { 
  2.         if (mStackBoxId != relativeStackBoxId) { 
  3.             if (mStack !=null) { 
  4.                 return null
  5.             } 
  6.             TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight); 
  7.             if (stack !=null) { 
  8.                 return stack; 
  9.             } 
  10.             return mSecond.split(stackId, relativeStackBoxId, position, weight); 
  11.         } 
  12.         TaskStack stack = new TaskStack(mService, stackId, mDisplayContent); 
  13.         TaskStack firstStack; 
  14.         TaskStack secondStack; 
  15.         if (position == TASK_STACK_GOES_BEFORE) { 
  16.             position = TASK_STACK_TO_LEFT_OF; 
  17.         } else if (position == TASK_STACK_GOES_AFTER) { 
  18.             // TODO: Test Configuration here for LTR/RTL. 
  19.             position = TASK_STACK_TO_RIGHT_OF; 
  20.         } 
  21.         switch (position) { 
  22.             default
  23.             case TASK_STACK_TO_LEFT_OF: 
  24.             case TASK_STACK_TO_RIGHT_OF: 
  25.                 mVertical = false
  26.                 if (position == TASK_STACK_TO_LEFT_OF) { 
  27.                     mWeight = weight; 
  28.                     firstStack = stack; 
  29.                     secondStack = mStack; 
  30.                 } else
  31.                     mWeight = 1.0f - weight; 
  32.                     firstStack = mStack; 
  33.                     secondStack = stack; 
  34.                 } 
  35.                 break
  36.             case TASK_STACK_GOES_ABOVE: 
  37.             case TASK_STACK_GOES_BELOW: 
  38.                 mVertical = true
  39.                 if (position == TASK_STACK_GOES_ABOVE) { 
  40.                     mWeight = weight; 
  41.                     firstStack = stack; 
  42.                     secondStack = mStack; 
  43.                 } else
  44.                     mWeight = 1.0f - weight; 
  45.                     firstStack = mStack; 
  46.                     secondStack = stack; 
  47.                 } 
  48.                 break
  49.         } 
  50.         mFirst = new StackBox(mService, mDisplayContent,this); 
  51.         firstStack.mStackBox = mFirst; 
  52.         mFirst.mStack = firstStack; 
  53.  
  54.         mSecond = new StackBox(mService, mDisplayContent,this); 
  55.         secondStack.mStackBox = mSecond; 
  56.         mSecond.mStack = secondStack; 
  57.  
  58.         mStack = null
  59.         return stack; 
  60.     } 
  61. </span> 
<span style="font-size:18px;">    TaskStack split(int stackId, int relativeStackBoxId, int position, float weight) {
        if (mStackBoxId != relativeStackBoxId) {
            if (mStack != null) {
                return null;
            }
            TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight);
            if (stack != null) {
                return stack;
            }
            return mSecond.split(stackId, relativeStackBoxId, position, weight);
        }
        TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
        TaskStack firstStack;
        TaskStack secondStack;
        if (position == TASK_STACK_GOES_BEFORE) {
            position = TASK_STACK_TO_LEFT_OF;
        } else if (position == TASK_STACK_GOES_AFTER) {
            // TODO: Test Configuration here for LTR/RTL.
            position = TASK_STACK_TO_RIGHT_OF;
        }
        switch (position) {
            default:
            case TASK_STACK_TO_LEFT_OF:
            case TASK_STACK_TO_RIGHT_OF:
                mVertical = false;
                if (position == TASK_STACK_TO_LEFT_OF) {
                    mWeight = weight;
                    firstStack = stack;
                    secondStack = mStack;
                } else {
                    mWeight = 1.0f - weight;
                    firstStack = mStack;
                    secondStack = stack;
                }
                break;
            case TASK_STACK_GOES_ABOVE:
            case TASK_STACK_GOES_BELOW:
                mVertical = true;
                if (position == TASK_STACK_GOES_ABOVE) {
                    mWeight = weight;
                    firstStack = stack;
                    secondStack = mStack;
                } else {
                    mWeight = 1.0f - weight;
                    firstStack = mStack;
                    secondStack = stack;
                }
                break;
        }
        mFirst = new StackBox(mService, mDisplayContent, this);
        firstStack.mStackBox = mFirst;
        mFirst.mStack = firstStack;

        mSecond = new StackBox(mService, mDisplayContent, this);
        secondStack.mStackBox = mSecond;
        mSecond.mStack = secondStack;

        mStack = null;
        return stack;
    }
</span>
分裂的结果只有两种情况:

①分裂节点的StackBox.mStack转移到新的mFirst.mStack中,mSecond.mStack=new TaskStack(mService, stackId, mDisplayContent);

②分裂节点的StackBox.mStack转移到新的mSecond.mStack中,mFirst.mStack=new TaskStack(mService, stackId, mDisplayContent);

上面两种情况共同点是分裂节点StackBox.mStack会置null。

上述代码还可以归纳出,分裂节点就是二叉树的叶节点,只有叶节点才可以分裂,只有叶节点mStack变量才不为null。也可以说一个StackBox叶节点对应一个TaskStack。

从Android4.4源码来看,目前默认显示屏DEFAULT_DISPLAY的DisplayContent拥有两棵StackBox二叉树,这两个StackBox二叉树都还没有进行分裂过,只包含一个根节点。

8、TaskStack类

StackBox二叉树树的一个叶节点对应有一个TaskStack。

①TaskStack.mStackId,“stackId The id of the new TaskStack to create.”

②TaskStack.mTasks,保存着这个Task栈(TaskStack)中的所有Task。一个TaskStack中可以包含很多Task任务。

③TaskStack.mDimLayer、TaskStack.mAnimationBackgroundSurface

DimLayer对象,用来实现阴影效果的Surface包装类对象,弹出任何dialog,理应将dialog之下的窗口上面加一个阴影,也就是将dialog设置一个WindowManager.LayoutParams.FLAG_DIM_BEHIND属性mAnimationBackgroundSurface跟背景动画相关。

  1. TaskStack(WindowManagerService service, int stackId, DisplayContent displayContent) { 
  2.     mService = service; 
  3.     mStackId = stackId; 
  4.     mDisplayContent = displayContent; 
  5.     mDimLayer = new DimLayer(service,this); 
  6.     mAnimationBackgroundSurface = new DimLayer(service,this); 
    TaskStack(WindowManagerService service, int stackId, DisplayContent displayContent) {
        mService = service;
        mStackId = stackId;
        mDisplayContent = displayContent;
        mDimLayer = new DimLayer(service, this);
        mAnimationBackgroundSurface = new DimLayer(service, this);
    }

④TaskStack.mDimWinAnimator

保存着阴影效果Surface的窗口动画,在startDimmingIfNeeded()函数中更新WindowStateAnimator。

  1. void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) { 
  2.     // Only set dim params on the highest dimmed layer. 
  3.     final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator; 
  4.     // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 
  5.     if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator ==null 
  6.             || !existingDimWinAnimator.mSurfaceShown 
  7.             || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) { 
  8.         mDimWinAnimator = newWinAnimator; 
  9.     } 
    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
        // Only set dim params on the highest dimmed layer.
        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
                || !existingDimWinAnimator.mSurfaceShown
                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
            mDimWinAnimator = newWinAnimator;
        }
    }
mDimWinAnimator中保存的动画与其他窗口的动画是同一个对象,并不独立拥有WindowStateAnimator

⑤TaskStack.mAnimationBackgroundSurface

⑥TaskStack.mAnimationBackgroundAnimator

上面两个跟背景动画相关?

9、mTaskHistory

保存着所有TaskStack中的Task合集。

10、mExitingTokens、mExitingAppTokens

mExitingTokens:* Window tokens that are in the process of exiting, but still on screen for animations.*

mExitingAppTokens: * Application tokens that are in the process of exiting, but still on screen for animations.*

对应延迟remove的WindowToken、AppWindowToken分别保存在mExitingTokens和mExitingAppTokens中。在下一次调用performLayoutAndPlaceSurfacesLockedInner()时便从这两个list中移除满足一定条件的Token。

11、mWindows

保存着属于该DisplayContent的按Z轴高度排列的所有WindowState。

12、createStack()

函数用来创建一个TaskStack。

  1. TaskStack createStack(int stackId,int relativeStackBoxId, int position,float weight) { 
  2.     TaskStack newStack = null
  3.     if (DEBUG_STACK) Slog.d(TAG,"createStack: stackId=" + stackId + " relativeStackBoxId=" 
  4.             + relativeStackBoxId + " position=" + position +" weight=" + weight); 
  5.     if (stackId == HOME_STACK_ID) { 
  6.         if (mStackBoxes.size() != 1) { 
  7.             throw new IllegalArgumentException("createStack: HOME_STACK_ID (0) not first."); 
  8.         } 
  9.         newStack = mHomeStack; 
  10.     } else
  11.         int stackBoxNdx; 
  12.         for (stackBoxNdx = mStackBoxes.size() -1; stackBoxNdx >= 0; --stackBoxNdx) { 
  13.             final StackBox box = mStackBoxes.get(stackBoxNdx); 
  14.             if (position == StackBox.TASK_STACK_GOES_OVER 
  15.                     || position == StackBox.TASK_STACK_GOES_UNDER) { 
  16.                 // Position indicates a new box is added at top level only. 
  17.                 if (box.contains(relativeStackBoxId)) { 
  18.                     StackBox newBox = new StackBox(mService,this, null); 
  19.                     newStack = new TaskStack(mService, stackId,this); 
  20.                     newStack.mStackBox = newBox; 
  21.                     newBox.mStack = newStack; 
  22.                     final int offset = position == StackBox.TASK_STACK_GOES_OVER ?1 : 0
  23.                     if (DEBUG_STACK) Slog.d(TAG,"createStack: inserting stack at "
  24.                             (stackBoxNdx + offset)); 
  25.                     mStackBoxes.add(stackBoxNdx + offset, newBox); 
  26.                     break
  27.                 } 
  28.             } else
  29.                 // Remaining position values indicate a box must be split. 
  30.                 newStack = box.split(stackId, relativeStackBoxId, position, weight); 
  31.                 if (newStack !=null) { 
  32.                     break
  33.                 } 
  34.             } 
  35.         } 
  36.         if (stackBoxNdx < 0) { 
  37.             throw new IllegalArgumentException("createStack: stackBoxId " + relativeStackBoxId 
  38.                     + " not found."); 
  39.         } 
  40.     } 
  41.     if (newStack != null) { 
  42.         layoutNeeded = true
  43.     } 
  44.     EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, relativeStackBoxId, position, 
  45.             (int)(weight *100 + 0.5)); 
  46.     return newStack; 
    TaskStack createStack(int stackId, int relativeStackBoxId, int position, float weight) {
        TaskStack newStack = null;
        if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackBoxId="
                + relativeStackBoxId + " position=" + position + " weight=" + weight);
        if (stackId == HOME_STACK_ID) {
            if (mStackBoxes.size() != 1) {
                throw new IllegalArgumentException("createStack: HOME_STACK_ID (0) not first.");
            }
            newStack = mHomeStack;
        } else {
            int stackBoxNdx;
            for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
                final StackBox box = mStackBoxes.get(stackBoxNdx);
                if (position == StackBox.TASK_STACK_GOES_OVER
                        || position == StackBox.TASK_STACK_GOES_UNDER) {
                    // Position indicates a new box is added at top level only.
                    if (box.contains(relativeStackBoxId)) {
                        StackBox newBox = new StackBox(mService, this, null);
                        newStack = new TaskStack(mService, stackId, this);
                        newStack.mStackBox = newBox;
                        newBox.mStack = newStack;
                        final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
                        if (DEBUG_STACK) Slog.d(TAG, "createStack: inserting stack at " +
                                (stackBoxNdx + offset));
                        mStackBoxes.add(stackBoxNdx + offset, newBox);
                        break;
                    }
                } else {
                    // Remaining position values indicate a box must be split.
                    newStack = box.split(stackId, relativeStackBoxId, position, weight);
                    if (newStack != null) {
                        break;
                    }
                }
            }
            if (stackBoxNdx < 0) {
                throw new IllegalArgumentException("createStack: stackBoxId " + relativeStackBoxId
                        + " not found.");
            }
        }
        if (newStack != null) {
            layoutNeeded = true;
        }
        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, relativeStackBoxId, position,
                (int)(weight * 100 + 0.5));
        return newStack;
    }

position大体上分为两大类型,导致有两种创建TaskStack方式:①直接new一棵只有根节点的StackBox树,再new一个TaskStack;②从StackBox.mStackBoxId==relativeStackBoxId的叶节点上分裂出两个StackBox,再new一个TaskStack保存在两个中的一个StackBox中。目前看来android4.4的第二个TaskStack是通过第一种方式创建的。

ActivityStackSupervisor.startActivityUncheckedLocked()-->ActivityStackSupervisor.adjustStackFocus()-->ActivityManagerService.createStack(-1, HOME_STACK_ID, StackBox.TASK_STACK_GOES_OVER, 1.0f);研究下这串代码调用会发现AMS那边对应会new ActivityStack,也就是说AMS中的一个ActivityStack对应WMS中的一个TaskStack,且是一一对应关系。这种对应关系体现在TaskStack.mStackId==ActivityStack.mStackId。

13、moveHomeStackBox()

从名字就可以看出是干嘛的,移动Home对应的StackBox函数。mStackBoxes中有序的保存着StackBox二叉树根节点,因此肯定会有调换顺序函数,这个函数就是。从moveHomeStackBox()函数中还可以知道目前只支持两颗StackBox树。

  1. boolean moveHomeStackBox(boolean toTop) { 
  2.     if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop + " Callers="
  3.             Debug.getCallers(4)); 
  4.     EventLog.writeEvent(EventLogTags.WM_HOME_STACK_MOVED, toTop ? 1 : 0); 
  5.     switch (mStackBoxes.size()) { 
  6.         case 0:throw new RuntimeException("moveHomeStackBox: No home StackBox!"); 
  7.         case 1:return false;// Only the home StackBox exists. 
  8.         case 2
  9.             if (homeOnTop() ^ toTop) { 
  10.                 mStackBoxes.add(mStackBoxes.remove(0)); 
  11.                 return true
  12.             } 
  13.             return false
  14.         default: thrownew RuntimeException("moveHomeStackBox: Too many toplevel StackBoxes!"); 
  15.     } 
    boolean moveHomeStackBox(boolean toTop) {
        if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop + " Callers=" +
                Debug.getCallers(4));
        EventLog.writeEvent(EventLogTags.WM_HOME_STACK_MOVED, toTop ? 1 : 0);
        switch (mStackBoxes.size()) {
            case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!");
            case 1: return false; // Only the home StackBox exists.
            case 2:
                if (homeOnTop() ^ toTop) {
                    mStackBoxes.add(mStackBoxes.remove(0));
                    return true;
                }
                return false;
            default: throw new RuntimeException("moveHomeStackBox: Too many toplevel StackBoxes!");
        }
    }

14、setTouchExcludeRegion()

这个函数主要作用是设置目标TaskStack的触摸区域mTouchExcludeRegion值,为多窗口设计的。

WMS.performLayoutAndPlaceSurfacesLockedInner()-->WMS.setFocusedStackFrame()-->setTouchExcludeRegion()

16、switchUserStacks()

这个函数在多用户切换时调用。

  1. void switchUserStacks(int oldUserId,int newUserId) { 
  2.     final WindowList windows = getWindowList(); 
  3.     for (int i =0; i < windows.size(); i++) { 
  4.         final WindowState win = windows.get(i); 
  5.         if (win.isHiddenFromUserLocked()) { 
  6.             if (DEBUG_VISIBILITY) Slog.w(TAG,"user changing " + newUserId + " hiding " 
  7.                     + win + ", attrs=" + win.mAttrs.type +", belonging to " 
  8.                     + win.mOwnerUid); 
  9.             win.hideLw(false); 
  10.         } 
  11.     } 
  12.  
  13.     for (int stackBoxNdx = mStackBoxes.size() -1; stackBoxNdx >= 0; --stackBoxNdx) { 
  14.         mStackBoxes.get(stackBoxNdx).switchUserStacks(newUserId); 
  15.     } 
    void switchUserStacks(int oldUserId, int newUserId) {
        final WindowList windows = getWindowList();
        for (int i = 0; i < windows.size(); i++) {
            final WindowState win = windows.get(i);
            if (win.isHiddenFromUserLocked()) {
                if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing " + newUserId + " hiding "
                        + win + ", attrs=" + win.mAttrs.type + ", belonging to "
                        + win.mOwnerUid);
                win.hideLw(false);
            }
        }

        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
            mStackBoxes.get(stackBoxNdx).switchUserStacks(newUserId);
        }
    }
函数调用isHiddenFromUserLocked()判断窗口是否能够在新用户中显示,返回true就调用WindowState.hideLw()隐藏窗口。
同时会调用StackBox.switchUserStacks()-->TaskStack.switchUser(),但是这个 switchUser()逻辑有问题吧??

  1. void switchUser(int userId) { 
  2.     int top = mTasks.size(); 
  3.     for (int taskNdx =0; taskNdx < top; ++taskNdx) { 
  4.         Task task = mTasks.get(taskNdx); 
  5.         if (task.mUserId == userId) { 
  6.             mTasks.remove(taskNdx); 
  7.             mTasks.add(task); 
  8.             --top; 
  9.         } 
  10.     } 

 类似资料: