DisplayContent、StackBox、TaskStack笔记

时间:2022-09-26 23:10:58

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

每一个显示设备,都有一个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;
}
}
}

完。