赞
踩
在Android系统中,窗口是有分组概念的,例如,Activity中弹出的所有PopupWindow会随着Activity的隐藏而隐藏,可以说这些都附属于Actvity的子窗口分组,对于Dialog也同样如此,只不过Dialog与Activity属于同一个分组。之间已经简单介绍了窗口类型划分:应用窗口、子窗口、系统窗口,Activity与Dialog都属于应用窗口,而PopupWindow属于子窗口,Toast、输入法等属于系统窗口。只有应用窗口与系统窗口可以作为父窗口,子窗口不能作为子窗口的父窗口,也就说Activity与Dialog或者系统窗口中可以弹出PopupWindow,但是PopupWindow不能在自己内部弹出PopupWindow子窗口。日常开发中,一些常见的问题都同窗口的分组有关系,比如为什么新建Dialog的时候必须要用Activity的Context,而不能用Application的;为什么不能以PopupWindow的View为锚点弹出子PopupWindow?其实这里面就牵扯都Android的窗口组织管理形式,本文主要包含以下几点内容:
窗口的Z次序管理:窗口的分配序号、次序调整等
虽然我们看到的手机屏幕只是一个二维平面X*Y,但其实Android系统是有隐形的Z坐标轴的,其方向与手机屏幕垂直,与我们的实现平行,所以并不能感知到。
前面分析了窗口分组的时候涉及了两个对象WindowState与Windtoken,但仅限分组,分组无法决定窗口的显示的Z-order,那么再WMS是怎么管理所有窗口的Z-order的? 在WMS中窗口被抽象成WindowState,因此WindowState内部一定有属性来标志这个窗口的Z-order,实现也确实如此,WindowState采用三个个int值mBaseLayer+ mSubLayer + mLayer 来标志窗口所处的位置,前两个主要是根据窗口类型确定窗口位置,mLayer才是真正的值,定义如下:
final class WindowState implements WindowManagerPolicy.WindowState {
final WindowList mChildWindows = new WindowList();
final int mBaseLayer;
final int mSubLayer;
<!--最终Z次序的赋值-->
int mLayer;
}
从名字很容知道mBaseLayer是标志窗口的主次序,面向的是一个窗口组,而mSubLayer主要面向单独窗口,要来标志一个窗口在一组窗口中的位置,对两者来说值越大,窗口越靠前,从此final属性知道,两者的值是不能修改的,而mLayer可以修改,对于系统窗口,一般不会同时显示两个,因此,可以用主序决定,比较特殊的就是Activity与子窗口,首先子窗口的主序肯定是父窗口决定的,子窗口只关心次序就行。而父窗口的主序却相对麻烦,比如对于应用窗口来说,他们的主序都是一样的,因此还要有一个其他的维度来作为参考,比如对于Activity,主序都是一样的,怎么定他们真正的Z-order呢?其实Activity的顺序是由AMS保证的,这个顺序定了,WMS端Activity窗口的顺序也是定了,这样下来次序也方便定了。
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
...
<!--关键点1 子窗口类型的Z order-->
if ((mAttrs.type >= FIRST_SUB_WINDOW &&
mAttrs.type <= LAST_SUB_WINDOW)) {
mBaseLayer = mPolicy.windowTypeToLayerLw(
attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
mAttachedWindow = attachedWindow; final WindowList childWindows = mAttachedWindow.mChildWindows;
final int numChildWindows = childWindows.size();
if (numChildWindows == 0) {
childWindows.add(this);
} else {
...
} else {
<!--关键点2 普通窗口类型的Z order-->
mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = 0;
mAttachedWindow = null;
mLayoutAttached = false;
}
...
}
由于窗口所能选择的类型是确定的,因此mBaseLayer与mSubLayer所能选择的值只有固定几个,很明显这两个参数不能精确的确定Z-order,还会有其他微调的手段,也仅限微调,在系统层面,决定了不同类型窗口所处的位置,比如系统Toast类型的窗口一定处于所有应用窗口之上,不过我们最关心的是Activity类的窗口如何确定Z-order的,在new WindowState之后,只是粗略的确定了Activity窗口的次序,看一下添加窗口的示意代码:
addWindow(){
<!--1-->
new WindowState
<!--2-->
addWindowToListInOrderLocked(win, true);
<!--3-->
assignLayersLocked(displayContent.getWindowList());
}
新建state对象之后,Z-order还要通过addWindowToListInOrderLocked及assignLayersLocked才能确定,addWindowToListInOrderLocked主要是根据窗口的Token找到归属,插入到对应Token的WindowState列表,如果是子窗口还要插入到父窗口的对应位置中:
插入到特定位置后其实Z-order就确定了,接下来就是通过assignLayersLocked为WindowState分配真正的Z-order mLayer,
private final void assignLayersLocked(WindowList windows) {
int N = windows.size();
int curBaseLayer = 0;
int curLayer = 0;
int i;
boolean anyLayerChanged = false;
for (i=0; i<N; i++) {
final WindowState w = windows.get(i);
final WindowStateAnimator winAnimator = w.mWinAnimator;
boolean layerChanged = false;
int oldLayer = w.mLayer;
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
|| (i > 0 && w.mIsWallpaper)) {
<!--通过偏移量-->
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
w.mLayer = curLayer;
}
if (w.mLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
}
...
}
mLayer最终确定后,窗口的次序也就确定了,这个顺序要最终通过后续的relayout更新到SurfaceFlinger服务,之后,SurfaceFlinger在图层混排的时候才知道如何处理。
WMS中窗口次序分配如何影响SurfaceFlinger服务
SurfaceFlinger在图层混排的时候应该不会混排所有的窗口,只会混排可见的窗口,比如有多个全屏Activity的时候,SurfaceFlinger只会处理最上面的,那么SurfaceFlinger如何知道哪些窗口可见哪些不可见呢?前文分析了WMS分配Z-order之后,要通过setLayer更新到SurfaceFlinger,接下来看具体流程,创建SurfaceControl之后,会创建一次事务,确定Surface的次序:
SurfaceControl.openTransaction();
try {
mSurfaceX = left;
mSurfaceY = top;
try {
mSurfaceControl.setPosition(left, top);
mSurfaceLayer = mAnimLayer;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null) {
mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
}
<!--设置次序-->
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setAlpha(0);
mSurfaceShown = false;
} catch (RuntimeException e) {
mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
}
mLastHidden = true;
} finally {
SurfaceControl.closeTransaction();
}
}
这里通过openTransaction与closeTransaction保证一次事务的完整性,中间就Surface次序的调整,closeTransaction会与SurfaceFlinger通信,通知SurfaceFlinger更新Surface信息,这其中就包括Z-order。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。