WindowManagerService的窗口布局过程,是在窗口完成“添加”addWindow 之后,确定其最终在屏幕上的尺寸、位置和层级(Z-Order)的关键环节。这个过程主要由应用进程的 ViewRootImpl 触发,核心实现在WMS的 relayoutWindow 和 performLayout 等方法中。
触发时机:何时开始布局?
窗口布局的触发时机很灵活,每当窗口属性发生变化时都可能触发。最常见的场景是:
- 应用首次添加窗口后:在
ViewRootImpl.setView()中会调用requestLayout(),通过Choreographer发起一个异步遍历任务mTraversalRunnable。 - 窗口属性变化时:比如应用因键盘弹出、屏幕旋转等调用
requestLayout()重新布局。
//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 方法,携带应用期望的窗口尺寸等参数。
//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参数传回给应用。
//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;
}
}
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);
}
...
}
Binder.restoreCallingIdentity(origId);
return result;
}
WindowSurfacePlacer.performSurfacePlacement() 是 Android 窗口管理服务(WindowManagerService)中最核心的方法之一。它的主要职责是计算并应用所有窗口的最终状态——包括它们的位置、大小、可见性以及 Z 轴顺序,并将这些变更通过一次原子操作提交给底层渲染系统 SurfaceFlinger,最终完成屏幕的刷新。
这个过程可以理解为一次全局的窗口布局“提交”(commit),其核心流程大致如下:
//frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
// 1. 检查是否处于延迟布局状态,如果是则记录请求并返回
//`mDeferDepth`:表示延迟布局的嵌套深度,用于合并多次请求
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
// 2. 执行一个循环,最多6次,用于处理递归或依赖性的布局请求
// `6次循环`:这是一个保护机制,用于处理布局过程中可能产生的新的布局请求。
//Google工程师的经验值认为6次足以处理所有正常的嵌套场景,超过则可能有问题,
//会停止循环并报错
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop(); // 核心布局逻辑
...
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
...
}
performSurfacePlacementLoop() 这个方法承担了具体的布局工作,其核心是调用 RootWindowContainer.performSurfacePlacement() 开始遍历整个窗口树。
private void performSurfacePlacementLoop() {
//如果正在布局中,则直接返回,避免递归调用导致崩溃或死锁
if (mInLayout) {
return;
}
final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
//系统配置(如屏幕方向)还在变更中,直接返回
if (defaultDisplay.mWaitingForConfig) {
return;
}
if (!mService.mDisplayReady) {
return;
}
mInLayout = true;
//在开始布局之前删除僵尸窗口。僵尸窗口是
//指持有者已经异常退出或处于隐藏状态却拥有surface窗口
if (!mService.mForceRemoves.isEmpty()) {
//mForceRemoves 僵尸窗口
while (!mService.mForceRemoves.isEmpty()) {
final WindowState ws = mService.mForceRemoves.remove(0);
//立即移除,不经过常规的动画和清理流程
ws.removeImmediately();
}
Object tmp = new Object();
synchronized (tmp) {
try {
tmp.wait(250);
} catch (InterruptedException e) {
}
}
}
try {
//`RootWindowContainer`会遍历DisplayContent下的所有窗口,
//根据最新的状态(尺寸、层级、动画等)重新计算它们的位置和大小,
//并提交相应的 `Transaction` 给 SurfaceFlinger 更新显示
mService.mRoot.performSurfacePlacement();
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
//继续请求下一轮布局,设置mTraversalScheduled为true
requestTraversal();
} else {
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
//通知监听者,窗口布局发生了变化
if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
}
} catch (RuntimeException e) {
mInLayout = false;
}
}
//遍历DisplayContent下的所有窗口,根据最新的状态(尺寸、层级、动画等)
//重新计算它们的位置和大小,并提交相应的 `Transaction` 给 SurfaceFlinger 更新显示
//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement() {
try {
performSurfacePlacementNoTrace();
} finally {
}
}
void performSurfacePlacementNoTrace() {
int i;
//更新处于焦点状态的窗口
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
...
//递增布局序列号
mWmService.mTransactionSequence++;
//获取手机屏幕的宽高尺寸
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
...
//使用Transaction,保证所有窗口的更新是一个原子操作,避免屏幕出现撕裂或闪烁。
mWmService.openSurfaceTransaction();
try {
//核心:计算并应用所有窗口的Surface变更
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
} finally {
//关闭事务
// 这一步会将当前累积的所有变更通过 `SurfaceControl` 的 `apply` 方法,
//一次性发送给 `SurfaceFlinger` 进程,由它来完成真正的屏幕合成与显示
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
}
}
...
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
// 再次更新焦点窗口mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
if (isLayoutNeeded()) {
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
}
//遍历所有需要调整大小的窗口,
//并通过 Binder 异步通知对应的应用进程(`IWindow.resized()`),
//让应用重新绘制内容。
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
//如果屏幕旋转等操作完成,解除冻结状态
}
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
//销毁不可见窗口的 Surface
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
// Time to remove any exiting tokens?
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
//移除过期的 Token,清理已退出的组件
displayContent.removeExistingTokensIfPossible();
}
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
//pendingLayoutChanges是一个位掩码,
//用于记录在当前布局循环中发生的特殊事件(如壁纸变化、需要重新布局)
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
}
}
...
final int N = mWmService.mPendingRemove.size();
if (N > 0) {
//批量移除除待删除窗口,并重新分配层级
if (mWmService.mPendingRemoveTmp.length < N) {
mWmService.mPendingRemoveTmp = new WindowState[N + 10];
}
mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
mWmService.mPendingRemove.clear();
ArrayList<DisplayContent> displayList = new ArrayList();
for (i = 0; i < N; i++) {
final WindowState w = mWmService.mPendingRemoveTmp[i];
w.removeImmediately();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
displayList.add(displayContent);
}
}
for (int j = displayList.size() - 1; j >= 0; --j) {
final DisplayContent dc = displayList.get(j);
//遍历列表并最终分配唯一的 `mLayer` 值,
//这个值才真正决定了窗口在屏幕上的 Z 轴顺序
dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
}
forAllDisplays(dc -> {
//通知 IMS 最新的窗口布局,以便正确分发触摸事件
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
dc.updateTouchExcludeRegion();
});
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
mWmService.enableScreenIfNeededLocked();
//向 `Choreographer` 注册一个回调,
请求在下一个 VSYNC(垂直同步)信号到来时执行动画。
mWmService.scheduleAnimationLocked();
}
遍历并应用变更 (applySurfaceChangesTransaction)
-
遍历窗口树:系统会从
DisplayContent(代表一个逻辑显示屏)开始,递归遍历其下的所有WindowState(代表一个窗口)。 -
执行布局和绘制状态变更:对于每个窗口,系统会:
- 根据最新的布局参数,计算并更新其
Surface的大小和位置(layoutWindow流程)。 - 检查窗口的绘制状态,例如,当窗口的
Surface已完成绘制,其状态会被更新为READY_TO_SHOW,进而变为HAS_DRAW,最终被设置为可见
- 根据最新的布局参数,计算并更新其
//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private void applySurfaceChangesTransaction() {
mHoldScreenWindow = null;
mObscuringWindow = null;
final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
...
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
//递归处理所有显示屏(包括主屏、外接屏、虚拟屏等),每个DisplayContent会:
//调整窗口层级(Z-order)
//更新窗口位置、大小、透明度等属性
//处理动画
//应用窗口的Surface操作
dc.applySurfaceChangesTransaction();
}
//给DisplayManagerService一个机会调整显示属性(如旋转角度),确保与窗口系统协调
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
//将`mDisplayTransaction`中的所有Surface操作合并到全局事务中
//最终提交给SurfaceFlinger进行合成
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
//执行布局和绘制状态变更
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear();
int repeats = 0;
do {
repeats++;
//最多重试6次(防止无限循环)
if (repeats > 6) {
clearLayoutNeeded();
break;
}
//壁纸需要重新调整
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
mWallpaperController.adjustWallpaperWindows();
}
//配置需要更新(如屏幕旋转)
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
if (updateOrientation()) {
setLayoutNeeded();
sendNewConfiguration();
}
}
//需要重新布局
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
setLayoutNeeded();
}
// FIRST LOOP: Perform a layout, if needed.
//小于4,如果循环四次以后还无法满足,则不再尝试对窗口布局,
//仅处理pendingLayoutChanges,如果6次尝试仍无法满足,则放弃尝试
if (repeats < LAYOUT_REPEAT_THRESHOLD) {
// 执行布局
performLayout(repeats == 1, false /* updateInputWindows */);
} else {
}
pendingLayoutChanges = 0;
try {
//DisplayPolicy修改一些属性状态
mDisplayPolicy.beginPostLayoutPolicyLw();
//mApplyPostLayoutPolicy 记录可能影响系统窗口或锁屏界面可见性窗口
//调用DisplayPolicy.applyPostLayoutPolicyLw()
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
//修改系统窗口或者锁屏界面的可见效
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0); //PhoneWindowManager认为布局结果仍需
进行调整,则重新再来一遍
mTmpApplySurfaceChangesTransactionState.reset();
try {
//遍历所有窗口,执行实际的Surface操作
//设置窗口位置、大小、透明度
//应用动画变换(旋转、缩放)
//设置Z-order(层级顺序)
//更新窗口裁剪区域
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
}
//提交Surface事务到SurfaceFlinger
//处理窗口绘制完成的通知
prepareSurfaces();
//检查是否需要显示输入法,在布局完成后触发
mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
if (!mWmService.mDisplayFrozen) {
//更新显示属性
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
}
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
//壁纸可见性通知mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
}
//检查Activity的所有窗口是否都已绘制完成,如果是,则触发显示逻辑(如启动过渡动画结束
while (!mTmpUpdateAllDrawn.isEmpty()) {
final ActivityRecord activity = mTmpUpdateAllDrawn.removeLast();
// See if any windows have been drawn, so they (and others associated with them)
// can now be shown.
activity.updateAllDrawn();
}
}
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void performLayout(boolean initial, boolean updateInputWindows) {
try {
performLayoutNoTrace(initial, updateInputWindows);
} finally {
}
}
//先对顶级窗口进行布局,然后对子窗口布局
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
//检查是否需要布局
if (!isLayoutNeeded()) {
return;
}
//清除布局标志,避免重复执行
clearLayoutNeeded();
//布局序列号更新
//布局序列号递增,用于:
//调试和日志追踪
//检测布局变化的顺序
//防止并发问题(每次布局有唯一ID)
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
//initial表示是否为首次布局通常用于动画初始状态的判断)
mTmpInitial = initial;
//对所有顶级窗口进行布局
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
//对非顶级窗口布局
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
//布局完成后窗口的尺寸和位置可能发生变化,此时需要输入系统更新窗口状态
mInputMonitor.setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
//非强制更新(有变化才更新)
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
}
private final Consumer<WindowState> mPerformLayout = w -> {
...
if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
...
//准备布局所需的临时数据
//处理窗口装饰(标题栏、边框)的尺寸计算
//更新窗口状态标志
//w = WindowState
w.prelayout();
...
//根据窗口类型(Activity、Dialog、System等)计算位置
//应用Gravity(对齐方式)
// 考虑系统Insets(状态栏、导航栏、手势区域)
//处理窗口缩放、旋转
// 计算最终的Frame矩形
//DisplayPolicy
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
...
}
};
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (w.mLayoutAttached) {
if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
|| w.mLayoutNeeded) {
...
//w = WindowState
w.prelayout();
//DisplayPolicy
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
}
}
};
DisplayPolicy.layoutWindowLw() 是 Android 窗口策略中为单个窗口计算最终位置和大小的核心方法。它在 DisplayContent.applySurfaceChangesTransaction() 的迭代布局阶段被调用,将布局策略“翻译”成每个窗口具体的矩形区域。
layoutWindowLw() 接收一个 WindowState 对象,综合其布局参数(如gravity、flags)、窗口类型(普通应用、系统窗口、输入法等)以及屏幕装饰区域(状态栏、导航栏等),计算出该窗口在屏幕上的精确位置和大小,并将结果保存到窗口的 WindowFrames 中。你可以把它看作是整个窗口布局流水线上的“最后一道精准工序”。
//frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
//DisplayFrames包含屏幕各种预定义区域(如安全区域、稳定区域、系统UI占据区域)的数据对象。
//如果当前窗口是系统导航栏,调用专门的 `layoutNavigationBar` 方法计算其位置。
//这个方法会返回导航栏的位置(底部/左侧/右侧),并保存到 `mNavigationBarPosition`
//中,然后直接返回。导航栏不参与后续的通用布局逻辑。
if (win == mNavigationBar) {
mNavigationBarPosition = layoutNavigationBar(displayFrames,
mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
//如果当前窗口是状态栏(`mStatusBar`),并且**不能**接收输入事件
//(`!canReceiveInput(win)`),则调用专门的 `layoutStatusBar` 计算其位置后返回。
//为什么判断条件里要加上“不能接收输入”?因为状态栏在某些场景下(如锁屏时)可能不接收
//输入,但它仍然需要被布局
if ((win == mStatusBar && !canReceiveInput(win))) {
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
//快速路径:画中画(PiP)过渡。如果该窗口所属的 Activity 正在等待进入画中画模式
//(`mWaitForEnteringPinnedMode` 为 `true`),则跳过布局,直接返回。这是因为
//PiP 过渡期间窗口的布局由动画系统单独控制,不应该被这里的逻辑干扰。
if (win.mActivityRecord != null && win.mActivityRecord.mWaitForEnteringPinnedMode) {
return;
}
//从 `WindowState` 中获取 `LayoutParams`,然后提取出:
final WindowManager.LayoutParams attrs = win.getAttrs();
//`type`:窗口类型(如 `TYPE_APPLICATION`、`TYPE_SYSTEM_ERROR` 等)。
final int type = attrs.type;
//`fl`:公共标志位(如 `FLAG_LAYOUT_IN_SCREEN`)。
final int fl = attrs.flags;
//`pfl`:私有标志位(如 `PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME`)。
final int pfl = attrs.privateFlags;
//`sim`:软输入模式(Soft Input Mode),控制窗口与输入法如何交互。
final int sim = attrs.softInputMode;
//**获取窗口自己的显示框架和窗口帧对象**。
//允许每个窗口返回自己独立的 `DisplayFrames`。对于自由窗口或分屏窗口,它们可能有
//不同于整个屏幕的显示区域。如果窗口没有自定义的,则直接返回传入的 `displayFrames`。
displayFrames = win.getDisplayFrames(displayFrames);
//windowFrames`:这是 `WindowState` 中保存所有帧(`mFrame`、`mParentFrame`、
//`mDisplayFrame` 等)的容器对象。
final WindowFrames windowFrames = win.getWindowFrames();
//将当前的 `mParentFrame` 保存到临时变量 `sTmpLastParentFrame` 中,
//用于后续比较是否发生了变化
sTmpLastParentFrame.set(windowFrames.mParentFrame);
// 创建 `pf` 和 `df` 引用,分别指向 `WindowFrames` 中
//的 `mParentFrame` 和 `mDisplayFrame`。注意,这里是对引用的操作,后续
//对 `pf` 和 `df` 的修改会直接影响 `windowFrames` 中的原始矩形。
final Rect pf = windowFrames.mParentFrame;
final Rect df = windowFrames.mDisplayFrame;
//重置一个标志,表示父帧尚未因屏幕挖孔而被裁剪。
windowFrames.setParentFrameWasClippedByDisplayCutout(false);
//窗口是否被允许在屏幕范围内布局(包括系统UI覆盖的区域)。
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
//窗口是否希望自动处理系统UI(状态栏/导航栏)的占位。
//这两个标志的组合决定了窗口如何与系统UI交互。
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
//**计算窗口的显示边界 (`df`)** 。
//获取窗口当前的边衬区(Insets)状态 `state`。
final InsetsState state = win.getInsetsState();
//根据窗口的布局参数、边衬区状态和窗口令牌的边界(`win.mToken.getBounds()`),
//**初步设置 `df`**。对于普通应用窗口,`df` 通常被设为整个屏幕;对于自由窗口或分屏窗
//口,`df` 会被设为该窗口的边界
computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
//计算父帧 (`pf`)
//如果窗口没有父窗口
if (attached == null) {
pf.set(df);
//输入法避让
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
//表示希望父帧能避开输入法区域。于是会从 `state` 中获取输入法(IME)的边衬区源
//(`InsetsSource`),计算输入法在 `pf` 上占据的矩形区域,然后从 `pf` 中减去
//(`inset`)这部分区域。这样,当输入法弹出时,窗口的父帧会相应缩小,从而让窗口内容
//自动上移,避免被输入法遮挡。
final InsetsSource source = state.peekSource(ITYPE_IME);
if (source != null) {
// 减去输入法区域
pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
}
}
} else {
//如果窗口有父窗口
//如果子窗口没有设置 `FLAG_LAYOUT_IN_SCREEN`,那么它的父帧等于**父窗口的帧**
//(`attached.getFrame()`),意味着它被限制在父窗口内部。
//如果子窗口设置了 `FLAG_LAYOUT_IN_SCREEN`,那么它的父帧等于 `df`,意味着它
//可以在全屏范围内布局(但受到后续裁剪的限制)。,
除非明确要求"布局在屏幕中"(`FLAG_LAYOUT_IN_SCREEN`)
pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
}
final int cutoutMode = attrs.layoutInDisplayCutoutMode;
//**开始处理屏幕挖孔(DisplayCutout)** 。
//获取窗口的 `layoutInDisplayCutoutMode` 属性,它有三个可能的值:
// `DEFAULT`:默认模式,窗口内容避开挖孔区域。
// `NEVER`:窗口内容永远不能与挖孔重叠。
// `ALWAYS`:窗口内容允许延伸到挖孔区域。
//只有当模式不是 `ALWAYS` 时,才需要执行裁剪逻辑(因为 `ALWAYS` 模式不限制窗口
//扩展到挖孔区域)。
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
//窗口是否附加在父窗口中并且没有设置 `FLAG_LAYOUT_IN_SCREEN`(即它是在父窗口
//内布局的)。
final boolean attachedInParent = attached != null && !layoutInScreen;
//窗口是否**没有**请求隐藏状态栏
//(即 `getRequestedVisibility(ITYPE_STATUS_BAR)`
//返回 `false`,表示状态栏可见)。注意方法名 `getRequestedVisibility` 返
//回 `true` 表示请求**隐藏**,所以取反后表示“希望全屏”或“不隐藏状态栏”。
final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
//窗口是否**没有**请求隐藏导航栏(即导航栏可见)。
final boolean requestedHideNavigation =
!win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
//判断窗口是否为“浮动的全屏窗口”。一个窗口同时满足以下条件时被认为是浮动的:
//不是全屏窗口(`!attrs.isFullscreen()`)、设置
// 了 `FLAG_LAYOUT_IN_SCREEN`,并且不是基础应用窗口
// (`TYPE_BASE_APPLICATION`)。这种窗口会在后续被裁剪或移位到 `displayFrame内。
final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
&& type != TYPE_BASE_APPLICATION;
//创建一个临时矩形 `displayCutoutSafeExceptMaybeBars`,
//其初始值为 `displayFrames.mDisplayCutoutSafe`(即屏幕的安全区域,完全
//避开了所有挖孔区域)
final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
//**处理“短边”模式**。
//如果窗口的 `cutoutMode` 是 `SHORT_EDGES`,表示窗口可以在屏幕的“短边”
//方向扩展到挖孔区域。
if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
// //“短边”是上下两条边。因此将临时矩形的 `top` 和 `bottom` 设为极大/
//极小值,即移除上下边界限制。
displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
} else {
//“短边”是左右两条边。因此将临时矩形的 `left` 和 `right` 设为极大
///极小值。
displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
}
}
...
//如果窗口设置了 `FLAG_LAYOUT_NO_LIMITS` 标志(允许超出屏幕显示),
//并且不是系统错误窗口 (`TYPE_SYSTEM_ERROR`),也不在多窗口模式下,
//则将其显示帧设为极大的范围(`-10000` 到 `10000`),
//从而实现“可以绘制到屏幕外”的效果,常用于一些特殊动画或全屏效果
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
&& !win.inMultiWindowMode()) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
}
// 如果父帧在此次计算中发生了变化,会标记窗口内容已改变
if (!sTmpLastParentFrame.equals(pf)) {
windowFrames.setContentChanged(true);
}
//根据计算好的父帧和显示帧,最终确定窗口的精确坐标和大小,
//并更新相关的输入法源帧信息
win.computeFrameAndUpdateSourceFrame();
}
WindowState.computeFrame() 方法。这是窗口几何计算的最终执行者,将 layoutWindowLw() 中确定的约束(pf 和 df)转换为窗口的精确坐标和大小。
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void computeFrameAndUpdateSourceFrame() {
//核心计算
computeFrame();
//通常,只有输入法(IME)窗口、某些系统窗口或实现了 `InsetsControlTarget` 的窗口
//才拥有这个对象。它负责向系统提供窗口的边衬区信息,从而影响其他窗口的布局。例如,输入法
//窗口弹出时,它会向系统报告自己占据的区域,系统会据此调整其他应用窗口的大小。
if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) { //计算窗口当前的 `mSourceFrame`,并将其报告给 `InsetsStateController`。
//其他窗口(尤其是应用窗口)在布局时,会查询 `InsetsState` 来获取输入法、状态栏等系统
//窗口占据的区域,从而调整自己的内容区域,避免被遮挡
mControllableInsetProvider.updateSourceFrame();
}
}
void computeFrame() {
//如果此窗口即将被另一个窗口替换,窗口正在执行退出动画,尚未收到移除请求,直接返回
//不更新帧。这防止了在替换过程中对旧窗口应用新的布局,导致视觉闪烁
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
return;
}
// 标记 `mHaveFrame = true`,表示窗口已有有效的帧数据
mHaveFrame = true;
//获取窗口所属的任务(Task)
final Task task = getTask();
//窗口是否全屏且填充显示区域(不在多窗口模式,且匹配显示区域边界)
final boolean isFullscreenAndFillsArea = !inMultiWindowMode() && matchesDisplayAreaBounds();
final boolean windowsAreFloating = task != null && task.isFloating();
//显示内容(DisplayContent
final DisplayContent dc = getDisplayContent();
//显示设备的逻辑信息(宽度、高度等)
final DisplayInfo displayInfo = getDisplayInfo();
//窗口帧
final WindowFrames windowFrames = getLayoutingWindowFrames();
//用于边衬区计算的帧,初始设为窗口令牌的边界
mInsetFrame.set(getBounds());
final Rect layoutContainingFrame;
final Rect layoutDisplayFrame;
final int layoutXDiff;
final int layoutYDiff;
final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
final InsetsControlTarget imeTarget = dc.getImeTarget(IME_TARGET_LAYERING);
//判断当前窗口是否受输入法(IME)影响,以及是否需要进行位置调整
//imeWin:当前可见的输入法窗口
//imeTarget:当前接收输入法边衬区的目标窗口
//windowsAreFloating:窗口是否属于浮动任务(如自由窗口、画中画)
//判断当前窗口是否是输入法调整的目标
//对于浮动窗口:如果输入法目标存在,且与当前窗口属于同一个任务,则为目标
//对于非浮动窗口:调用 `isImeLayeringTarget()` 判断(通常是最顶层的应用窗口)。
final boolean isInputMethodAdjustTarget = windowsAreFloating
? imeTarget != null && task == imeTarget.getWindow().getTask()
: isImeLayeringTarget();
//只有当输入法窗口可见,且当前窗口是调整目标时,才为 `true`
final boolean isImeTarget =
imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
//这是 `computeFrame()` 中最复杂的部分,决定了窗口的 `mContainingFrame`——
//即窗口内容可以放置的“容器”边界。
if (isFullscreenAndFillsArea || layoutInParentFrame()) {
//全屏窗口,子窗口直接使用 `mParentFrame`
windowFrames.mContainingFrame.set(windowFrames.mParentFrame);
layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutContainingFrame = windowFrames.mParentFrame;
//偏移量 `layoutXDiff` 和 `layoutYDiff` 均为 0,表示不需要额外偏移
layoutXDiff = 0;
layoutYDiff = 0;
} else {
//非全屏窗口(含输入法调整)
//**初始包含帧**设为窗口令牌的边界(即任务边界)。
windowFrames.mContainingFrame.set(getBounds());
if (isImeTarget) {
//**自由窗口模式**
if (inFreeformWindowingMode()) {
//计算窗口的可见帧(排除系统栏、输入法和挖孔)
final InsetsState state = dc.getInsetsStateController().getRawInsetsState();
final Rect visibleFrame = mTmpRect;
visibleFrame.set(state.getDisplayFrame());
visibleFrame.inset(state.calculateInsets(visibleFrame,
systemBars() | ime() | displayCutout(), false /* ignoreVisibility */));
final int bottomOverlap =
windowFrames.mContainingFrame.bottom - visibleFrame.bottom;
//如果窗口底部超出了可见帧底部
if (bottomOverlap > 0) {
//则将窗口向上平移(`offset(0, -offs)`),使窗口内容不被输入法遮挡。
//但最多平移到顶部(`distanceToTop` 限制)。
final int distanceToTop = Math.max(windowFrames.mContainingFrame.top
- visibleFrame.top, 0);
int offs = Math.min(bottomOverlap, distanceToTop);
windowFrames.mContainingFrame.offset(0, -offs);
mInsetFrame.offset(0, -offs);
}
//**分屏/多窗口模式(非画中画)**
} else if (!inPinnedWindowingMode() && windowFrames.mContainingFrame.bottom
> windowFrames.mParentFrame.bottom) {
//如果包含帧底部超出了父帧底部,则将包含帧底部设为父帧底部,使得窗口在
//父帧内重新布局,避免被输入法推出父帧范围。
windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
}
}
//浮动窗口(画中画/自由窗口)
if (windowsAreFloating) {
如果包含帧为空,则设为 `mDisplayFrame`
if (windowFrames.mContainingFrame.isEmpty()) {
windowFrames.mContainingFrame.set(windowFrames.mDisplayFrame);
}
}
//**设置布局相关帧和偏移**
//保存原始的 `mDisplayFrame` 备份。
layoutDisplayFrame = mTmpRect2;
//将 `mDisplayFrame` 设为 `mContainingFrame`(显示帧被限制在包含帧内)。
layoutDisplayFrame.set(windowFrames.mDisplayFrame);
windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
//计算 `layoutXDiff` 和 `layoutYDiff`:`mInsetFrame` 相对
//于 `mContainingFrame` 的偏移量。
layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
//布局包含帧设为 `mInsetFrame`
layoutContainingFrame = mInsetFrame;
mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
//减去边衬区(`subtractInsets`)
//从 `mDisplayFrame` 中减去系统UI和边衬区占据的区域
subtractInsets(windowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
mTmpRect);
//对于非子窗
if (!layoutInParentFrame()) {
//从 `mContainingFrame` 和 `mInsetFrame` 中减去父帧边衬区的影响
subtractInsets(windowFrames.mContainingFrame, layoutContainingFrame,
windowFrames.mParentFrame, mTmpRect);
subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
mTmpRect);
}
//确保布局显示帧在布局包含帧内。
layoutDisplayFrame.intersect(layoutContainingFrame);
}
final int pw = windowFrames.mContainingFrame.width();
final int ph = windowFrames.mContainingFrame.height();
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
mLastRequestedWidth = mRequestedWidth;
mLastRequestedHeight = mRequestedHeight;
windowFrames.setContentChanged(true);
}
final int fw = windowFrames.mFrame.width();
final int fh = windowFrames.mFrame.height();
//根据窗口的 `gravity`、`width`、`height` 等属性,
//在 `layoutContainingFrame` 和 `layoutDisplayFrame` 的约束下,
//计算出 `mFrame` 的最终位置和大小
applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
//`TYPE_DOCK_DIVIDER`分屏分割线
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
//如果帧发生了变化
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
}
}
//应用偏移并设置兼容帧
//将所有帧按 `-layoutXDiff` 和 `-layoutYDiff` 反向偏移,以补偿之
//前 `mInsetFrame` 相对于 `mContainingFrame` 的偏移。这确保了最终帧在正确的位置。
windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
windowFrames.mCompatFrame.set(windowFrames.mFrame);
if (hasCompatScale()) {
//为支持屏幕兼容模式(如低分辨率应用在高分辨率屏幕上显示),创建 `mCompatFrame`。
//如果窗口有兼容缩放(`hasCompatScale()`),则将 `mCompatFrame` 乘以逆缩放因子,
//使其回到应用自身的坐标系。
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
//如果是壁纸窗口,且帧大小发生变化,则更新壁纸偏移量(`updateWallpaperOffset`),
//确保壁纸随着窗口大小变化正确滚动。
if (mIsWallpaper && (fw != windowFrames.mFrame.width()
|| fh != windowFrames.mFrame.height())) {
dc.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
}
//计算相对帧,保存窗口相对于父窗口(或父容器)的坐标
windowFrames.mRelFrame.set(windowFrames.mFrame);
WindowContainer parent = getParent();
int parentLeft = 0;
int parentTop = 0;
//对于子窗口
if (mIsChildWindow) {
//父窗口是 `WindowState`,取其 `mFrame` 的左上角。
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
//对于非子窗口
} else if (parent != null) {
//父容器是 `Task` 或 `DisplayContent`,取其 `getBounds()` 的左上角。
final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
//最终 `mRelFrame` 的左上角 = `mFrame.left - parentLeft`,
//`mFrame.top - parentTop`。
windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
windowFrames.mFrame.top - parentTop);
}
应用进程接收:执行View绘制
应用进程的 ViewRootImpl 收到WMS返回的结果后,会继续执行 performTraversals() 的后续步骤:
performMeasure():根据WMS计算的尺寸,测量View树中每个View的大小。performLayout():根据测量结果,为View树中的每个View确定在窗口内的位置。performDraw():利用WMS返回的Surface,将绘制内容渲染到缓冲区。
总而言之,WMS窗口布局的本质是一个从应用到系统服务,再回到应用的闭环过程。应用发起请求,WMS集中计算和分配资源,最后应用根据结果完成UI的绘制与渲染。