WindowManagerService的窗口布局过程,是在窗口完成"添加"addWindow 之后,确定其最终在屏幕上的尺寸、位置和层级(Z-Order)的关键环节 。这个过程主要由应用进程的 ViewRootImpl 触发,核心实现在WMS的 relayoutWindow 和 performLayout 等方法中。
触发时机:何时开始布局?
窗口布局的触发时机很灵活,每当窗口属性发生变化时都可能触发。最常见的场景是:
- 应用首次添加窗口后 :在
ViewRootImpl.setView()中会调用requestLayout(),通过Choreographer发起一个异步遍历任务mTraversalRunnable。 - 窗口属性变化时 :比如应用因键盘弹出、屏幕旋转等调用
requestLayout()重新布局。
scss
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
...
//请求首次布局
requestLayout();
...
}
}
}
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
...
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//MessageQueue 中插入一个同步屏障 。这个屏障的作用是:
//所有同步消息都不会被执行,只有异步消息可以绕过屏障继续执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//注册一个执行 View 的测量、布局、绘制的CallBack
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
//核心绘制流程
performTraversals();
...
}
}
跨进程请求:从应用到WMS (relayoutWindow)
mTraversalRunnable 的核心是执行 performTraversals(),其中会通过 mWindowSession.relayout() 跨进程调用WMS的 relayoutWindow 方法,携带应用期望的窗口尺寸等参数。
csharp
//frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
...
final boolean wasReportNextDraw = mReportNextDraw;
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
...
try {
...
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
} else {
...
maybeHandleWindowMove(frame);
}
...
mIsInTraversal = false;
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
//调用WMS.relayoutWindow()
int relayoutResult = mWindowSession.relayout(mWindow, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize);
mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
//relayoutWindow之后填充Surface
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
if (blastSurface != null) {
mSurface.transferFrom(blastSurface);
}
}
}
...
}
WMS核心处理:计算与分配
relayoutWindow 是WMS处理布局请求的入口,主要完成:
-
查找窗口状态 :通过
windowForClientLocked()找到对应的WindowState对象。 -
触发全面布局计算 :调用
WindowSurfacePlacer.performSurfacePlacement()。这是核心步骤,内部会:- 执行
performLayout():遍历所有窗口,分别处理非附加窗口 和附加窗口 (如PopupWindow),通过WindowManagerPolicy.layoutWindowLw()为每个窗口计算并保存最终的mFrame等坐标信息。 - 更新窗口层级 :通过
RootWindowContainer管理所有窗口(DisplayContent)的层级关系。
- 执行
-
创建或更新Surface :
relayoutWindow会为可见窗口调用WindowStateAnimator.createSurfaceLocked()创建SurfaceControl。最终通过mSurface.copyFrom(mSurfaceControl)将Surface传回给应用进程。 -
返回计算结果 :将计算好的窗口大小、位置等数据通过
relayout()的out参数传回给应用。
ini
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
...
synchronized (mGlobalLock) {
//根据 `client` 查找对应的 `WindowState`,若找不到则直接返回
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
...
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
//设置窗口大小为客户端需要的大小
win.setRequestedSize(requestedWidth, requestedHeight);
}
//`frameNumber` 用于同步帧的序列号(与 BLAST 同步机制相关)
win.setFrameNumber(frameNumber);
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
if (attrs != null) {
//根据系统状态(如是否锁屏、是否在沉浸模式)调整窗口参数
displayPolicy.adjustWindowParamsLw(win, attrs);
win.mToken.adjustWindowParams(win, attrs);
int disableFlags =
(attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
disableFlags = 0;
}
win.mDisableFlags = disableFlags;
...
//记录可见性变化,用于后续判断是否需要移动IME或壁纸
final int oldVisibility = win.mViewVisibility;
final boolean becameVisible =
(oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
&& viewVisibility == View.VISIBLE;
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& win.hasWallpaper();
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
}
win.mRelayoutCalled = true;
win.mInRelayout = true;
win.setViewVisibility(viewVisibility);
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
//只有当窗口可见且关联的 Activity 可见时,才真正执行relayout(创建/更新 Surface)
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
if (!win.mWillReplaceWindow) {
if (wallpaperMayMove) {
displayContent.mWallpaperController.adjustWallpaperWindows();
}
//如果窗口不可见但已有 Surface,且没有正在播放退出动画,则尝试启动退出动画
//启动退出动画后,Surface 会在动画结束后被释放
focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
}
}
if (shouldRelayout) {
try {
//创建 Surface
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
...
return 0;
}
}
// We may be deferring layout passes at the moment, but since the client is interested
// in the new out values right now we need to force a layout.
//####重点
//遍历整个窗口树,进行最终的层级计算、位置确定和 Surface 显示
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
if (shouldRelayout) {
//标记 `mHasSurface = true`
//如果窗口之前是隐藏的,设置 `mFirstTimeVisible = true`
//更新窗口的显示状态
//如果是输入法窗口且尚未注册,立即注册
result = win.relayoutVisibleWindow(result);
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& displayContent.mInputMethodWindow == null) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
win.adjustStartingWindowFlags();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
...
if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
//可见但没有执行 relayout(如 Activity 不可见),返回现有 Surface
winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
} else {
...
try {
// 窗口不可见,释放 Surface
outSurfaceControl.release();
} finally {
...
}
}
}
if (focusMayChange) {
//更新焦点窗口
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
//更新输入法窗口
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
if (toBeDisplayed) {
//IME 目标窗口可能因可见性或焦点变化而改变
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (wallpaperMayMove) {
//壁纸可能需要因窗口显示/隐藏而重新调整层级
displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
if (win.mActivityRecord != null) {
displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
configChanged = displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
final DisplayInfo rotatedDisplayInfo =
win.mToken.getFixedRotationTransformDisplayInfo();
//处理屏幕旋转
if (rotatedDisplayInfo != null) {
//告诉 SurfaceFlinger 该窗口的 Buffer 应该如何旋转 outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
} else {
outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
}
...
}
Binder.restoreCallingIdentity(origId);
return result;
}
应用进程接收:执行View绘制
应用进程的 ViewRootImpl 收到WMS返回的结果后,会继续执行 performTraversals() 的后续步骤:
performMeasure():根据WMS计算的尺寸,测量View树中每个View的大小。performLayout():根据测量结果,为View树中的每个View确定在窗口内的位置。performDraw():利用WMS返回的Surface,将绘制内容渲染到缓冲区。
总而言之,WMS窗口布局的本质是一个从应用到系统服务,再回到应用的闭环过程。应用发起请求,WMS集中计算和分配资源,最后应用根据结果完成UI的绘制与渲染。