WindowManagerService (3) 布局系统 - l-feng/l-feng.github.io GitHub Wiki
在上两节中,欣赏过了wms中有的关于wms的创建过程,以及对于创建过程中的重要的windowToken以及windowState有过了解。
在这章中主要了解下WMS的布局系统relayoutWindow() 函数修改了指定的布局参数,然后performLayoutAndPlaaceSurfaceLocked()
遍历所有窗口并对于他们进行重新布局
一:relayoutWindow()
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
Surface outSurface) {
...... //进行相关权限的检查
/*接下来的操作在做主mWindowMap的情况下完成,在wms中,几乎所有的操作都是在锁住mWindowMap的情况下完成的,在后面分析
performLayoutAndPlaaceSurfaceLocked函数时候就会发现,wms对于窗口的操作比较复杂,为了防止线程间的竞态发生,
wms会对于mWinsdowMap的所有窗口进行同步 */synchronized(mWindowMap) {
//从mWindowMap中获取需要进行的relayout的WindowState,relayoutWindow(会根据
自己的参数进行WindowState的更新
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
//接下来主要是根据用户传过来的参数更新WindowState的属性
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE && (win.mRequestedWidth != requestedWidth
|| win.mRequestedHeight != requestedHeight)) {
win.mLayoutNeeded = true;
win.mRequestedWidth = requestedWidth;
win.mRequestedHeight = requestedHeight;
}
......
final boolean scaledWindow =
((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);
if (scaledWindow) {
// requested{Width|Height} Surface's physical size
// attrs.{width|height} Size on screen
win.mHScale = (attrs.width != requestedWidth) ?
(attrs.width / (float)requestedWidth) : 1.0f;
win.mVScale = (attrs.height != requestedHeight) ?
(attrs.height / (float)requestedHeight) : 1.0f;
} else {
win.mHScale = win.mVScale = 1;
}
......
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
configChanged = updateOrientationFromAppTokensLocked(false);
//调用performLayoutAndPlaceSurfacesLocked();
performLayoutAndPlaceSurfacesLocked();
.....
//返回布局结果
outFrame.set(win.mCompatFrame);
outOverscanInsets.set(win.mOverscanInsets);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
outStableInsets.set(win.mStableInsets);
outOutsets.set(win.mOutsets);
inTouchMode = mInTouchMode;
......
}
......
return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
| (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
| (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0);
}
这段代码截取自网络,我没有仔细的去实现。根据自己的理解,relayoutWindow的主要作用流程是:
1:先对于窗口做一些权限的检查
2:根据用户传进来的参数对于windowState的参数进行更新
3:根据Window的可见性更新或者创建surface及启动动画
3.1 对于窗口可见的处理方式:
1) 如果窗口没有surface,则为其创建一个surface
2)如果客户端改变了窗口的色彩格式(有layoutParams.format指定)发生了改变,则为其重新创建一个指定格式的surface
3) 如果客户端尚未被显示出来,并且客户端已经完成了绘制,则为其启动一个淡入动画
3.2 对于窗口不可见的处理方式
1) 标记windowState的mExiting的属性为true
2) 如果窗口目前正在被显示出来,则为其启动一个淡出动画
3) 释放客户端所持有的Surface对象,自此之后,客户端无法在更新窗口的内容
5:最主要的一步便是通过performLayoutAndPlaceSurfacesLocked,它将遍历所有的窗口,为他们计算布局的尺寸,
并且将布局的尺寸设置给他们的Surface。
6:返回布局结果
7;向AMS更新configuration,因为屏幕可能发生旋转
通过以上的步骤便将布局的结果返回给客户端了,但是读者需要知道,我们一般所说的窗口属性主要是两种,
1:布局控制属性:这类属性主要是客户端需要设置的,用来想wms表达它所希望的窗口布局
2:布局结果属性:这类属性主要是在布局系统中计算出来的,直接影响到窗口的实际布局和显示,
客户端无法干预这些属性,只能被迫的接受这些布局结果,这些结果就是performLayoutAndPlaceSurfacesLocked计算出来的
二:performLayoutAndPlaceSurfacesLocked()函数分析
Private final void performLayoutAndPlaceSurfacesLocked(){
Int loopCount=6;
Do{
mTraversalSchduled=false;
performLayoutAndPlaceSurfaceLockedLoop();
mH.removeMessages(H.DO_TRAVERSAL);
loopCount--;
}while(mTraversalSchduled&&LoopCount>0);
}
从这段代码中可以看出主要实现的是performLayoutAndPlaceSurfaceLockedLoop()中完成的。
其中mTraversalSchduled的值主要是在requestTraversal里面的requestTraversalLocked中进行设置,
设置为true就说明需要在循环一次进行布局。并且在H.DO_TRAVERSAL的消息处理就是调用
performLayoutAndPlaceSurfacesLocked。requestTraversalLocked会在
performLayoutAndPlaceSurfaceLockedLoop中进行调用
三:performLayoutAndPlaceSurfaceLockedLoop()函数
该函数主要的流程如下:
1: 首先是安全性检测,这个函数禁止递归调用
2:主要释放僵尸窗口,并释放他所持有的surface,和GC类似。僵尸窗口主要是持有者已经异常退出或者处于隐藏状态却拥有surface的窗口。
当surface的操作类似于内存原因失败时,wms会将所有的僵尸窗口收集到mForceRemoves列表中,然后在这个阶段同一处理。
3:对于已经回收的内存进行标记。此次布局在发生surface操作异常时,不会再次尝试回收
4:调用了performLayoutAndPlaceSurfaceLockedInner()
5: 调用了requestTraversalLocked 。走到这步的条件是needslayout()并且mLayoutRequestCount<6。Needslayout()函数会遍历所有的
displayContent并且坚持它们的mLayoutNeeded字段的值。只要有一个displayContent需要重新布局,此函数会返回true。
mLayoutRequestCount<6 说明布局有可能重复6次也没有绘制完成。
6:通过监听者,窗口的布局发生了变化,目前的监听者只有一个ViewService
四:performLayoutAndPlaceSurfaceLockedInner()函数
在这个函数中主要实现了布局
Private void performLayoutAndPlaceSurfaceLockedInner(){
布局前的预处理;
遍历所有的DisplayContent{
遍历所有DisplayContent下的所有窗口{
对窗口进行布局;
}
对于布局结果进行检查,是否有必要重新对于DisplayContent执行布局;
对DisplayContent的布局后处理;
}
完成布局后的策略处理;
}
1:布局前的处理
1.1 首先更新处于焦点状态的窗口,将处于退出的Tokens设置为不可见
1.2 初始化mInnerFileds中的状态变量。客户端可以通过窗口的layoutParameters中的一些属性或者flag来指明屏幕的亮度
,按键背光的亮度,是否保持屏幕唤醒状态以及输入事件的超时时间,这些设置都可以在窗口的中找到与之对应的属性.
WMS使用mInnerFileds集合了这些状态,在布局过程中,wms会遍历所有窗口以查找这些设置,并保存在mInnerFileds中
1.3 递增布局序列号,wms每增加一次布局会导致序列号递增。appWindowToken中也保存了一个相应的序列号,在布局的过程中,
wms会通过对比这两个序列号以确定appWindowToken的状态是不是最新的
1.4 布局水印和StrictMode警告框
2:布局DisplayContent
DisplayContent的主要工作分为两个部分:
2.1 布局系统 使用do-while循环的主要工作是对DisplayContent所拥有的的窗口进行布局,其工作的侧重点在于PerformLayout。
主要采用performLayoutlockedinner()函数进行布局
2.2 布局后处理 后处理用户根据布局结果设置Surface的参数,应用一些动画效果等,工作侧重于PlaceSurfaces。主要的工作内容;
- 设置窗口的遮挡状态
2)从窗口的LayoutParams中提取关于屏幕亮度,键盘亮度,输入超时等设置
3)发起或取消Dimming效果
4)设置窗口Surface的位置和尺寸。
5)如果窗口的客户端已经完成对于Surface的绘制工作,则显示这个窗口。
完成布局处理之后,当前DisplayContent的布局就完成了,所有的窗口已经就位了。
五: performLayoutlockedinner()函数
1:通知WindowPolicyManager,使其为即将开始的布局操作做准备
Mpolicy.beginlayoutlw(isdefaultdisplay,dw,dh,mRoation);
2: 对所有的顶级窗口进行布局
2.1 调用prelayout()进行布局的准备
2.2 调用WindowManagerPolicy的LayoutWindowLw()函数对窗口进行布局
mpolicy.layoutWindowLw(win,win.attrs,null)
2.3 更新窗口的版本号
3:接着对于所有的非顶级窗口进行布局
布局完成后,窗口的尺寸和位置可能发生变化,此时,输入系统需要更新窗口的状态
4: 通知WindowManagerPolicy ,本次布局完成。
Mpolicy.finishlayoutLw();
六:布局的最后阶段
主要内容:
1:所有的DisplayContent已经完成布局
2:设置屏幕等各种参数
3:需要设置DisplayContent的LayoutNeeded设置为true
4:调用updatelayoutToAnimationlocked()启动动画系统。