首页 > 代码库 > 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笔记