首页 > 代码库 > DisplayContent、StackBox、TaskStack笔记

DisplayContent、StackBox、TaskStack笔记

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

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

1、DisplayContent()

<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();

<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

<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);

<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中(二叉树方式分裂)。

<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跟背景动画相关。

    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。

    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。

    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树。

    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()

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

    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()逻辑有问题吧??

    void switchUser(int userId) {
        int top = mTasks.size();
        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
            Task task = mTasks.get(taskNdx);
            if (task.mUserId == userId) {
                mTasks.remove(taskNdx);
                mTasks.add(task);
                --top;
            }
        }
    }

完。

DisplayContent、StackBox、TaskStack笔记