窗口中容器类介绍:
本节内容较多,建议结合前面的内容一起阅读:
1)、WindowContainer:
java
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>{
........................
}
WindowContainer的定义如上,可见其是一个容器,容器内的元素是自己或自己的子类(如:RootWindowContainer、DisplayContent、DisplayArea、DisplayArea.Tokens、TaskDisplayArea、Task、ActivityRecord、WindowToken、WindowState),而WindowContainer类继承自ConfigurationContainer类,该类也是一个容器类,其元素为自己或自己的子类。
其中有一些属性比较重要:mParent和mChildren,分别代表父节点和子节点。其中子节点是WindowList类型的,其排列顺序是依据其在z轴上的高度排列的,尾部的节点在z轴上最高最容易被用户看到。
2)、RootWindowContainer:
java
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
WindowContainer的根容器,可通过该节点遍历到窗口树上的任意窗口。他的mChildren继承自WindowContainer,所以该List的元素为DisplayContent。
3)、DisplayContent:
java
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
对应显示屏幕的概念,一个屏幕对应一个DisplayContent。一般情况下都是只有一个。在同一个DisplayContent中的窗口都会显示在同一个屏幕内。
4)、WindowToken:
java
class WindowToken extends WindowContainer<WindowState> {
WindowToken类是WindowState的容器,WindowState之前说过,在WMS中代表了一个窗口。
5)、ActivityRecord:
java
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
该类继承自WindowToken,所以也是WindowState类的容器。
6)、WindowState:
java
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
InsetsControlTarget, InputTarget {
其实WindowState也可以作为其他WindowState的容器;
7)、WallpaperWindowToken:
java
class WallpaperWindowToken extends WindowToken {
该类同样继承自WindowToken;
对以上4)-7)进行统一分析,所有的窗口分三大类:系统窗口、应用窗口、其他窗口;其中系统窗口在最上层,应用窗口次之,而系统窗口的容器则为WindowToken,应用窗口为ActivityRecord。而App以下的窗口,父容器就为WallpaperWindowToken。
8)、DisplayArea.Tokens:
是WindowToken的父容器,同时会在其中对WindowToken进行排序;
java
public static class Tokens extends DisplayArea<WindowToken> {
private final Comparator<WindowToken> mWindowComparator =
Comparator.comparingInt(WindowToken::getWindowLayerFromType);//这就是排序算法的核心。
}
排序算法触发的前提就是addChild函数,和前一节说的一样,找到addChild函数:
java
void addChild(WindowToken token) {
addChild(token, mWindowComparator);
}
这里可以看到在addChild的时候,通过mWindowComparator为WindowToken确认顺序,而mWindowComparator的初始化是通过WindowToken.java中的getWindowLayerFromType方法作为比较器的。最后的方法实现在WindowManagerPolicy.java中的getWindowLayerFromTypeLw()方法。这里在addWindow的宏观概念一文中有讲到,主要就是对比了窗口的类型,如果是应用窗口则直接返回2,如果是wallpaper窗口,直接返回1(在最底层);然后再根据窗口的类型返回其排序的权重,根据权重再决定该窗口在子窗口中该插入到哪个位置。其中这里的type是在WindowToken中的一个属性:从注释看就是WindowManager类中的LayoutParams属性,这个属性会在窗口初始化时确定。
java
/** The type of window this token is for, as per {@link WindowManager.LayoutParams} */
final int windowType;
9)、Task:
ActivityRecord的容器,作为应用窗口的容器,ActivityRecord也是需要容器的。
java
class Task extends TaskFragment {
从定义可见Task是继承自TaskFragment的一个类。而TaskFragment则也是继承自WindowContainer的类。
class TaskFragment extends WindowContainer {
Task可以是Task的容器,也可以是ActivityRecord的容器,最形象的理解就是多任务界面的一个窗口就是一个Task。TaskFragment这个类型我了解不多,只知道和平行视界相关。平行视界中两个Activity需要同时显示,Task实现不了这个功能,所以再Task和ActivityRecord之间加入了TaskFragment。而一般情况下因为TaskFragment是Task的父类,所以大家都会通过TaskFragment创建对象,但是最终创建的还是Task类型的对象
这里的排序的话比较简单:老规矩找到addchild方法。
java
void addChild(WindowContainer child, int index) {
index = getAdjustedChildPosition(child, index);
super.addChild(child, index);
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);
// A rootable task that is now being added to be the child of an organized task. Making
// sure the root task references is keep updated.
if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
getDisplayArea().addRootTaskReferenceIfNeeded((Task) child);
}
// Make sure the list of display UID allowlists is updated
// now that this record is in a new task.
mRootWindowContainer.updateUIDsPresentOnDisplay();
// Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be
// passed from Task constructor.下面应该就是平行视界这类相关的操作了
final TaskFragment childTaskFrag = child.asTaskFragment();
if (childTaskFrag != null && childTaskFrag.asTask() == null) {
childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);
// The starting window should keep covering its task when a pure TaskFragment is added
// because its bounds may not fill the task.
final ActivityRecord top = getTopMostActivity();
if (top != null) {
top.associateStartingWindowWithTaskIfNeeded();
}
}
}
这个方法比较直接,直接通过index指定了位置。虽然指定了位置,但是程序中还是要对其进行调整的。
java
private int getAdjustedChildPosition(WindowContainer wc, int suggestedPosition) {
final boolean canShowChild = wc.showToCurrentUser();
final int size = mChildren.size();
// Figure-out min/max possible position depending on if child can show for current user.
int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0;
int maxPosition = minPosition;
if (size > 0) {
maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
}
// Factor in always-on-top children in max possible position.
if (!wc.isAlwaysOnTop()) {
// We want to place all non-always-on-top containers below always-on-top ones.
while (maxPosition > minPosition) {
if (!mChildren.get(maxPosition).isAlwaysOnTop()) break;
--maxPosition;
}
}
// preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
return POSITION_BOTTOM;
} else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
return POSITION_TOP;
}
// Increase the maxPosition because children size will grow once wc is added.
if (!hasChild(wc)) {
++maxPosition;
}
// Reset position based on minimum/maximum possible positions.
return Math.min(Math.max(suggestedPosition, minPosition), maxPosition);
}
这里其实就是根据最大最小值和指定的值进行对比然后修正一下。
不过还有另一个形式的addChild算法:
java
void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
Task task = child.asTask();//这里如果child是task则返回一个值,否则返回null。
try {
if (task != null) {
task.setForceShowForAllUsers(showForAllUsers);
}
// We only want to move the parents to the parents if we are creating this task at the
// top of its root task.
addChild(child, toTop ? MAX_VALUE : 0, toTop /*moveParents*/);
} finally {
if (task != null) {
task.setForceShowForAllUsers(false);
}
}
}
这个算法参数都不同了,有三个参数,这里第一步直接判断子元素是不是task类型。如果是的话,就执行setForceShowForAllUsers该方法,但是这里也没啥,就是对mForceShowForAllUsers进行赋值,然后就是对其进行比较器为null的排序,这里就不重复讲了,比较的过程在WindowContainer.java中的protected void addChild(E child, Comparator comparator) 该方法中。
10)、TaskDisplayArea:
Task或者TaskDisplayArea的容器。继承自DisplayArea类。代表的是屏幕上一块专门用来存放app窗口的区域。其父容器是DisplayContent。
java
final class TaskDisplayArea extends DisplayArea<WindowContainer> {
这个类对其中元素排序的方法依然可以通过addChild方法去查看,
void addChild(WindowContainer child, int position) {
if (child.asTaskDisplayArea() != null) {
if (DEBUG_ROOT_TASK) {
Slog.d(TAG_WM, "Set TaskDisplayArea=" + child + " on taskDisplayArea=" + this);
}
super.addChild(child, position);
} else if (child.asTask() != null) {
addChildTask(child.asTask(), position);
} else {
throw new IllegalArgumentException(
"TaskDisplayArea can only add Task and TaskDisplayArea, but found "
+ child);
}
}
其对于两个子类的元素有着不同的排序方法。第一种就是直接调用WindowContainer.java中的addChild(E child, int index)方法,直接在指定位置添加。
第二个的话就是如果元素是task类的话其实和上面也差不多,就是通过当前已有的一些子类对指定的位置进行一些修正。
11)、DisplayArea:
该类用于将WindowContainer分组到DisplayContent下方的容器。DisplayArea是被DisplayAreaPolicy管理的,能够复写Configuration和被绑定到leash上,并且可以嵌套DisplayArea,该类有三种风格:
BELOW_TASKS:只能包含位于任务下方的BELLOW_TASK显示区域和WindowToken。
ABOVE_TASKS:只能包含位于任务上方的ABOVE_TASK显示区域和WindowToken。
ANY:可以包含任何类型的DisplayArea,以及任何类型的WindowToken或Task容器。
12)、RootDisplayArea:
DisplayArea的根节点。根据注释来看:从等级制度上是DisplayArea的根节点。同时也可以是 逻辑屏幕上DisplayContent的根节点,或者逻辑屏幕上一个群组DisplayAreaGroup的根节点。
java
/**
* Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
* of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
* logical display.
*/
class RootDisplayArea extends DisplayArea.Dimmable {
所以这里将屏幕上的窗口分为两个逻辑,一个是等级制度,一个是物理屏幕。
13)、DisplayAreaGroup:
继承自RootDisplayArea,可以理解为一个集群,一个DisplayArea和DisplayContent之间的一个集群。 DisplayContent是整个屏幕上DisplayArea的根节点,但是一部分DisplayArea也可以挂载在DisplayAreaGroup上 。
java
/** The root of a partition of the logical display. */
class DisplayAreaGroup extends RootDisplayArea {
14)、另外还有一个容器ImeContainer:
这是输入法相关的,我暂时没了解,看定义大概能知道也是一类WindowToken的容器。
java
private static class ImeContainer extends DisplayArea.Tokens {
综上最后得到一个关系图:其中我将一个层级的都用同一个颜色标注出来。
然后再通过类图对其中涉及到的类进行归类。