WindowManagerService 详解

Posted by Young Ken on 2017-06-26

WindowManagerService 分析

windowManagerService(简称WMS)是Android中比较重要的概念,它是管理系统中所有的窗体,管理他们的大小,位置,绘制层数等问题。

addWindow分析

从方法命名可以看出,Manager是管理窗体的,addWindow是添加窗体的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
WindowState attachedWindow = null;
synchronized(mWindowMap) {
//得到窗口要添加的DisplayContent
final DisplayContent displayContent = getDisplayContentLocked(displayId);
WindowToken token = mTokenMap.get(attrs.token);
if (token == null) {
}
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility,displayContent);
}
}

这个代码涉及到比较重要的概念就是WindowToken,WindowState,DisplayCopntent。

WindowToken 分析
1
2
3
4
5
6
7
WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) {
service = _service;
token = _token;
windowType = type;
explicit = _explicit;
}

从构造函数上分析,比较简单,而且要注意token就是一个IBinder对象。

addWindowToken()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void addWindowToken(IBinder token, int type) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.get(token);
if (wtoken != null) {
Slog.w(TAG_WM, "Attempted to add existing input method token: " + token);
return;
}
wtoken = new WindowToken(this, token, type, true);
mTokenMap.put(token, wtoken);
}
}

这个比较简单,先验证权限,如果token为空创建一个WindowToken。添加到mTokenMap中。

WindowState分析

WindowState是一个窗口的所有属性。下面具体分析

窗口的显示顺序

在android上除了xy还有一个默认的z,这样窗体就有了在窗体上显示的层次。

WindowState分析

1
2
3
4
5
6
7
8
9
10
11
12
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
if ((mAttrs.type >= FIRST_SUB_WINDOW &&
mAttrs.type <= LAST_SUB_WINDOW)) {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.windowTypeToLayerLw(
attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
}

mBaseLayer被叫做主序,mSubLayer被叫做子序。

主序越大,窗口及及其子窗口越靠前。

子序越大,窗口相对于其他兄弟窗口越靠前。

通过主序和子序确定窗口的次序

在回头看看addWindow的代码

1
2
3
4
5
6
7
8
9
10
11
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
if (type == TYPE_INPUT_METHOD_DIALOG)
addWindowToListInOrderLocked(win, true);
}
mLayersController.assignLayersLocked(displayContent.getWindowList());
}

addWindowToListInOrderLocked是将新创建的WindowState按照一定的顺序char当前的DisplayContent的mWindows列表中。

assignLayersLocked是将根据mWindow的存储顺序对所有的WindowState的主序和子序进行调整。

窗口布局

relayoutWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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, Rect outBackdropFrame,
Configuration outConfig, Surface outSurface) {
//得到WindowState并更新WindowState
WindowState win = windowForClientLocked(session, client, false);
//根据Window的可见性更新或者创建suerface启动动画
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
//创建surface
result = createSurfaceControl(outSurface, result, win, winAnimator);
}else {
}
//更新窗口焦点,壁纸
if (focusMayChange) {
}
//
configChanged = updateOrientationFromAppTokensLocked(false);
//便利所有的displayContent的窗口,从新计算布局尺寸
mWindowPlacerLocked.performSurfacePlacement();
//返回结果,布局结果返回
outFrame.set(win.mCompatFrame);
outOverscanInsets.set(win.mOverscanInsets);
outContentInsets.set(win.mContentInsets);
}

客户端只能强迫接收这个结果,因为客户端调用了relayoutWindow()的时候要求的布局尺寸和实际设置的有区别,Android的类ViewRootImpl采用可协商的方式绘制控件。在窗体显示之前,ViewRootImpl对整个控件树进行测量,得到一个理想的尺寸,并且将这个relayoutWindow()的requestedWindth/Height的参数交个WMS,布局之后会把WMS的最终结果交个ViewRootImpl,ViewRootImpl被迫接受这样的结果。

performSurfacePlacement分析

1
2
3
4
5
6
7
8
9
10
11
12
13
final void performSurfacePlacement() {
if (mDeferDepth > 0) {
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mH.removeMessages(DO_TRAVERSAL);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mWallpaperActionPending = false;
}

包含一个do while循环,循环的跳出条件是mTraversalScheduled == true和loopCount > 0。并且调用了

performSurfacePlacementLoop()

performSurfacePlacementLoop 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void performSurfacePlacementLoop() {
//释放内存
boolean recoveringMemory = false;
if (!mService.mForceRemoves.isEmpty()) {
recoveringMemory = true;
// Wait a little bit for things to settle down, and off we go.
while (!mService.mForceRemoves.isEmpty()) {
WindowState ws = mService.mForceRemoves.remove(0);
Slog.i(TAG, "Force removing: " + ws);
mService.removeWindowInnerLocked(ws);
}
Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
Object tmp = new Object();
synchronized (tmp) {
try {
tmp.wait(250);
} catch (InterruptedException e) {
}
}
}
try {
//
performSurfacePlacementInner(recoveringMemory);
if (mService.needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
}
}
}
}

mService.needsLayout()返回true和mLayoutRepeatCount小于6会调用requestTraversal()。回头再看

performSurfacePlacementInner

performSurfacePlacementInner分析

由于这个函数太复杂,参考大神的伪代码

1
2
3
4
5
6
7
8
9
private void performSurfacePlacementInner(boolean recoveringMemory) {
for DisplayContent {
for DisplayContent`s Windiow {
layout window;
}
检查窗体是否从新布局;
}
完成布局策略;
}