本篇基于Android Q代码,建议先看从一个Dialog的创建看Android的窗口机制(上篇)和从一个Dialog的创建看Android的窗口机制(下篇)大致了解窗口的添加机制再看本篇
本篇主要分析Android窗口的Z轴计算规则以及根据Z轴对WindowState的排列规则,从之前两篇关于Dialog的分析知道了窗口的添加最终会到WMS的addWindow方法中,窗口的Z轴计算就是在addWindow方法中进行的,Z轴越大越靠前
WMS.addWindow
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
......
//每个窗口在WMS都会对应一个WindowState,描述了窗口相关信息
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
......
//将窗口添加到WindowContainer的WindowList中,并进行排序
//这里的WindowContainer有两种类型:AppWindowToken和WindowToken
win.mToken.addWindow(win);
......
}
addWindow这个方法在从一个Dialog的创建看Android的窗口机制(下篇)已经进行过详细分析,我们这篇主要来详细看下上面这两部分
WindowState
WindowState中用mBaseLayer描述窗口的Z轴数,这个值只会和窗口的类型有关,mSubLayer描述子窗口的Z轴数
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
//省略赋值代码
......
//如果是子窗口
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
//计算窗口mBaseLayer,传入的是父窗口
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//计算窗口mSubLayer,传入的是当前窗口的类型
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
//根据自定义的比较器sWindowSubLayerComparator对子窗口排序
parentWindow.addChild(this, sWindowSubLayerComparator);
....
} else {
//计算窗口mBaseLayer
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//非子窗口mSubLayer为0
mSubLayer = 0;
mIsChildWindow = false;
.....
}
//如果是应用窗口并且mShowForAllUsers为true
if (mAppToken != null && mAppToken.mShowForAllUsers) {
//添加FLAG_SHOW_WHEN_LOCKED,代表可以显示在锁屏上
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
}
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
}
先看子窗口的分支
WindowManagerPolicy
计算mBaseLayer,有三十多种类型,根据不同类型返回不同的值,例如壁纸为1,Toast为8,屏保为14等等,很好理解,mBaseLayer的计算还是很简单的
default int getWindowLayerLw(WindowState win) {
return getWindowLayerFromTypeLw(win.getBaseType(), win.canAddInternalSystemWindow());
}
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
//如果是应用窗口
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER; //2
}
switch (type) {
case TYPE_WALLPAPER:
return 1;
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
return APPLICATION_LAYER;
case TYPE_DOCK_DIVIDER:
return APPLICATION_LAYER;
case TYPE_QS_DIALOG:
return APPLICATION_LAYER;
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
case TYPE_VOICE_INTERACTION:
return 5;
case TYPE_INPUT_CONSUMER:
return 6;
case TYPE_SYSTEM_DIALOG:
return 7;
case TYPE_TOAST:
return 8;
case TYPE_PRIORITY_PHONE:
return 9;
case TYPE_SYSTEM_ALERT:
return canAddInternalSystemWindow ? 13 : 10;
case TYPE_APPLICATION_OVERLAY:
return 12;
case TYPE_DREAM:
return 14;
......
default:
Slog.e("WindowManager", "Unknown window type: " + type);
return APPLICATION_LAYER; //2
}
}
最后计算出来的mBaseLayer还会乘上一个乘数因子
TYPE_LAYER_MULTIPLIER等于10000,TYPE_LAYER_OFFSET等于1000,例如普通的应用Activity计算下来 mBaseLayer = 2 * 10000 + 1000
乘上这么大一个数是为了给同类型的窗口保留足够的空间以至于不会出现重复
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
接着看mSubLayer的计算,同样是根据不同子窗口的类型返回不同的值,比如APPLICATION_PANEL_SUBLAYER等于FIRST_SUB_WINDOW + 3,FIRST_SUB_WINDOW等于1000
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
default int getSubWindowLayerFromTypeLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER; //1003
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER; //-2
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER; //-1
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER; //2
case TYPE_APPLICATION_ABOVE_SUB_PANEL:
return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER; //3
}
Slog.e("WindowManager", "Unknown sub-window type: " + type);
return 0;
}
自定义排序规则
接着看子窗口的添加规则
parentWindow.addChild(this, sWindowSubLayerComparator),通过自定义的比较器对子窗口进行排序,哪两个窗口排序?当前添加的窗口会和WindowList中的所有窗口比较,这里的所有窗口指的是同一父窗口下的所以窗口
private static final Comparator<WindowState> sWindowSubLayerComparator =
new Comparator<WindowState>() {
@Override
public int compare(WindowState w1, WindowState w2) {
final int layer1 = w1.mSubLayer;
final int layer2 = w2.mSubLayer;
if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
/*
对两个窗口的计算出来的mSubLayer进行比较
w1代表当前添加的窗口,w2代表已存在的窗口
当前窗口的mSubLayer小于已存在的窗口的mSubLayer或者
两者相等且都小于0则返回-1
*/
return -1;
}
return 1;
};
};
WindowContainer.addChild
WindowState并没有覆盖父类的addChild方法,mChildren是WindowList类型容器,继承自ArrayList
protected void addChild(E child, Comparator<E> comparator) {
//如果getParent()不为空代表已经添加过了
if (child.getParent() != null) {
throw new IllegalArgumentException("addChild: container=" + child.getName()
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
//初始化positionToAdd为-1
int positionToAdd = -1;
if (comparator != null) {
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
//把当前窗口的mSubLayer与mChildren里的所有窗口比较,只要mChildren中
//有一个窗口的mSubLayer大于当前窗口就可以结束比较了
//因为mChildren里总是按照mSubLayer大小顺序排列的
if (comparator.compare(child, mChildren.get(i)) < 0) {
positionToAdd = i;
break;
}
}
}
//positionToAdd等于-1说明当前添加的窗口的mSubLayer大于等于
//所有mChildren里的窗口
if (positionToAdd == -1) {
//则直接添加到mChildren的尾部
mChildren.add(child);
} else {
//否则将当前子窗口插入比较的那个窗口所在的位置
mChildren.add(positionToAdd, child);
}
//回调
onChildAdded(child);
//这个方法最终会通过SurfaceControl的Transaction类的setLayer
//方法,再通过JNI调用SurfaceFlinger的客户端代理SurfaceComposerClient
//将窗口Z轴设置到SurfaceFlinger中去
child.setParent(this);
}
总结一下子窗口的Z轴计算和窗口排列规则:
Z轴计算:子窗口的mBaseLayer由父窗口的type决定,根据对应type获取一个值然后乘上一个因子1000再加上1000,常见的应用Activity的窗口就是21000,子窗口还有一个mSubLayer,这个值根据子窗口自己的类型来获取的
窗口排列:子窗口添加到WindowContainer中的WindowList规则是根据mSubLayer的大小进行排序,大于或等于会依次排列在WindowList尾部,小于则插入对应比较的窗口所在的位置,所以WindowList是按顺序从小到大排列的
再回到WindowState构造方法看下非子窗口的分支,非子窗口很简单,根据自己窗口的type计算mBaseLayer,并将mSubLayer赋值为0
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
//省略赋值代码
......
//如果是子窗口
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
//计算窗口mBaseLayer,传入的是父窗口
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//计算窗口mSubLayer,传入的是当前窗口的类型
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
//根据自定义的比较器sWindowSubLayerComparator对子窗口排序
parentWindow.addChild(this, sWindowSubLayerComparator);
....
} else {
//计算窗口mBaseLayer
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//非子窗口mSubLayer为0
mSubLayer = 0;
mIsChildWindow = false;
.....
}
//如果是应用窗口并且mShowForAllUsers为true
if (mAppToken != null && mAppToken.mShowForAllUsers) {
//添加FLAG_SHOW_WHEN_LOCKED,代表可以显示在锁屏上
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
}
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
}
我们可以看到非子窗口并没有在WindowState构造方法中将自己添加到WindowList中,那在哪里添加的呢?我们回到WMS的addWindow方法
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
......
//每个窗口在WMS都会对应一个WindowState,描述了窗口相关信息
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
......
//将窗口添加到WindowContainer的WindowList中,并进行排序
win.mToken.addWindow(win);
......
}
win.mToken.addWindow(win)
mToken分为两种,应用类型窗口对应AppWindowToken,系统类型窗口对应WindowToken,AppWindowToken是WindowToken的子类,AppWindowToken的addWindow方法其实也会调到父类WindowToken的addWindow方法,我们就直接看WindowToken的addWindow方法
void addWindow(final WindowState win) {
if (DEBUG_FOCUS) Slog.d(TAG_WM,
"addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
//此窗口是子窗口直接return,子窗口已经在WindowState构造方法中添加了
if (win.isChildWindow()) {
return;
}
//此窗口没有向WindowList添加过
if (!mChildren.contains(win)) {
//通过自定义比较器mWindowComparator对窗口进行排序
addChild(win, mWindowComparator);
//窗口添加之后代表窗口已经发生变化
mWmService.mWindowsChanged = true;
}
}
非子窗口的添加和子窗口添加最终流程都一样,都会调到WindowContainer的addChild方法,只是比较器不同对应的窗口排序规则不同
mWindowComparator
private final Comparator<WindowState> mWindowComparator =
(WindowState newWindow, WindowState existingWindow) -> {
final WindowToken token = WindowToken.this;
//窗口mToken和token类型一致
if (newWindow.mToken != token) {
throw new IllegalArgumentException("newWindow=" + newWindow
+ " is not a child of token=" + token);
}
if (existingWindow.mToken != token) {
throw new IllegalArgumentException("existingWindow=" + existingWindow
+ " is not a child of token=" + token);
}
return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
};
isFirstChildWindowGreaterThanSecond
这个方法子类AppWindowToken进行覆写了,应用窗口和系统窗口的排序规则不一样,我们先看简单的,系统窗口的排序规则:直接对两个系统窗口的mBaseLayer进行比较,和子窗口的排序方式类似,mBaseLayer大的排在WindowList尾部
protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
WindowState existingWindow) {
return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
}
应用窗口排序规则:
@Override
protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
WindowState existingWindow) {
final int type1 = newWindow.mAttrs.type;
final int type2 = existingWindow.mAttrs.type;
//如果添加的窗口是最基本的窗口类型,并且WindowList里的窗口不是此类型
//则return false,就是-1,按照WindowContainer的排序规则,返回-1代表
//需要将此添加的窗口插入到比较的窗口所在位置
if (type1 == TYPE_BASE_APPLICATION && type2 != TYPE_BASE_APPLICATION) {
return false;
//反之则返回true,即将新添加的窗口按顺序添加在WindowList的尾部
} else if (type1 != TYPE_BASE_APPLICATION && type2 == TYPE_BASE_APPLICATION) {
return true;
}
// 如果添加的窗口是启动窗口类型,并且WindowList里的窗口不是此类型
//则返回true,即将新添加的窗口按顺序添加在WindowList的尾部
if (type1 == TYPE_APPLICATION_STARTING && type2 != TYPE_APPLICATION_STARTING) {
return true;
//反之则返回false,即将新添加的窗口插入到比较的窗口的位置
} else if (type1 != TYPE_APPLICATION_STARTING && type2 == TYPE_APPLICATION_STARTING) {
return false;
}
//其他普通的应用类型窗口都返回true,即按顺序添加到
//WindowList尾部
return true;
}
需要注意的是以上的排序指的是同一个token下的一组WindowState的排序,例如一个AppWindowToken下可能会有一组子窗口和一组应用类型窗口
WindowList就是一个普通的ArrayList,里面的窗口越靠尾部代表层级越高,越靠前代表层级越低,最终窗口的合成就是按照WindowList中的顺序,屏幕最上层的窗口在WindowList的尾部,屏幕最底层窗口在WindowList头部,所以窗口比较器中返回false代表此窗口Z轴小于与之比较的窗口的Z轴,就应该插入在它前面,返回true代表Z轴最高应该放在尾部
举个例子【1,3,5,7】,如果来一个数2,按照比较器规则,在它和3比较时返回false则应该插入3的前面,来个数字10,遍历所以数字都是返回true,则应该添加到尾部,我们以上分析的Z轴顺序只是在WMS一侧的,SurfaceFlinger一侧的窗口顺序和WMS对应的,通过SurfaceControl的Transaction类的setLayer方法,再通过JNI调用SurfaceFlinger的客户端代理SurfaceComposerClient将窗口Z轴设置到SurfaceFlinger中去,我们就不去细看了
最终WindowState的Z轴排序规则就是:
1.对三种不同类型的窗口都会自定义一个比较器用来排序
2.系统类型窗口比较器规则按照mBaseLayer从小到大排列
3.如果是应用类型窗口则对于TYPE_BASE_APPLICATION窗口总是放在最底下,对于TYPE_APPLICATION_STARTING窗口总是放在最上面,其他则按顺序添加到WindowList尾部,例如Activity和Dialog就按顺序添加
4.对于子窗口因为mBaseLayer都一样,则需要再依靠mSubLayer进行排序