RootWindowContainer
void performSurfacePlacementNoTrace() {
......
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mWmService.openSurfaceTransaction();
try {
// step1 处理每个窗口
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
}
checkAppTransitionReady(surfacePlacer);
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
if (isLayoutNeeded()) {
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
}
// 窗口尺寸变化通知client端
handleResizingWindows();
// 清理不可见的窗口,解除冻屏等
......
mWmService.scheduleAnimationLocked();
}
step1 处理每个窗口 RootWindowContainer#applySurfaceChangesTransaction()
private void applySurfaceChangesTransaction() {
mHoldScreenWindow = null;
mObscuringWindow = null;
// TODO(multi-display): Support these features on secondary screens.
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);
// step2 调用DisplayContent#applySurfaceChangesTransaction()
dc.applySurfaceChangesTransaction();
}
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
step2 调用DisplayContent#applySurfaceChangesTransaction()
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear();
int repeats = 0;
do {
repeats++;
if (repeats > 6) {
clearLayoutNeeded();
break;
}
......
// FIRST LOOP: Perform a layout, if needed.
if (repeats < LAYOUT_REPEAT_THRESHOLD) {
// step 2.1 DisplayContent#performLayout()
performLayout(repeats == 1, false /* updateInputWindows */);
} else {
}
// FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
pendingLayoutChanges = 0;
try {
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
}
mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
try {
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
}
prepareSurfaces();
......
}
step 2.1 DisplayContent#performLayout()
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
final boolean gone = w.isGoneForLayout();
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
if (mTmpInitial) {
w.resetContentChanged();
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
// layout之前的预处理,设置窗口的缩放参数
w.prelayout();
final boolean firstLayout = !w.isLaidOut();
// 计算窗口尺寸 Step3 DisplayPolicy#layoutWindowLw
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
......
}
};
void performLayout(boolean initial, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
try {
performLayoutNoTrace(initial, updateInputWindows);
}
}
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
......
// 非子窗口
// First perform layout of any root windows (not attached to another window).
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// 子窗口
// Now perform layout of attached windows, which usually depend on the position of the
// window they are attached to. XXX does not deal with windows that are attached to windows
// that are themselves attached.
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
......
}
Step3 DisplayPolicy#layoutWindowLw
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
......
final WindowManager.LayoutParams attrs = win.getAttrs();
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
displayFrames = win.getDisplayFrames(displayFrames);
final WindowFrames windowFrames = win.getWindowFrames();
sTmpLastParentFrame.set(windowFrames.mParentFrame);
// 获取窗口的pf,df
final Rect pf = windowFrames.mParentFrame;
final Rect df = windowFrames.mDisplayFrame;
windowFrames.setParentFrameWasClippedByDisplayCutout(false);
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
// 计算窗口尺寸,去掉内衬区域
computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
if (attached == null) {
// 非子窗口 pf等于df
pf.set(df);
} else {
// 非子窗口 pf等于父窗口的尺寸
pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
}
......
// step4 调用windowState#computeFrameAndUpdateSourceFrame计算窗口的真实尺寸
win.computeFrameAndUpdateSourceFrame();
}
该方法是窗口尺寸计算的核心,判断是根窗口还是子窗口,去掉内衬区域,然后根据各种frame计算窗口真实尺寸
step4 调用windowState#computeFrameAndUpdateSourceFrame计算窗口的真实尺寸
void computeFrameAndUpdateSourceFrame() {
computeFrame();
......
}
void computeFrame() {
mHaveFrame = true;
final Task task = getTask();
final boolean isFullscreenAndFillsArea = !inMultiWindowMode() && matchesDisplayAreaBounds();
final boolean windowsAreFloating = task != null && task.isFloating();
final DisplayContent dc = getDisplayContent();
final DisplayInfo displayInfo = getDisplayInfo();
final WindowFrames windowFrames = getLayoutingWindowFrames();
mInsetFrame.set(getBounds());
// Denotes the actual frame used to calculate the insets and to perform the layout. When
// resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
// insets temporarily. By the notion of a task having a different layout frame, we can
// achieve that while still moving the task around.
final Rect layoutContainingFrame;
final Rect layoutDisplayFrame;
// The offset from the layout containing frame to the actual containing frame.
final int layoutXDiff;
final int layoutYDiff;
final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
final InsetsControlTarget imeTarget = dc.getImeTarget(IME_TARGET_LAYERING);
final boolean isInputMethodAdjustTarget = windowsAreFloating
? imeTarget != null && task == imeTarget.getWindow().getTask()
: isImeLayeringTarget();
final boolean isImeTarget =
imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
if (isFullscreenAndFillsArea || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
windowFrames.mContainingFrame.set(windowFrames.mParentFrame);
layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutContainingFrame = windowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
windowFrames.mContainingFrame.set(getBounds());
......
layoutDisplayFrame = mTmpRect2;
layoutDisplayFrame.set(windowFrames.mDisplayFrame);
windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
layoutContainingFrame = mInsetFrame;
mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
subtractInsets(windowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
mTmpRect);
if (!layoutInParentFrame()) {
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();
final int fw = windowFrames.mFrame.width();
final int fh = windowFrames.mFrame.height();
// step4.1 applyGravityAndUpdateFrame()
applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
// 设置mFrame偏移
// Offset the actual frame by the amount layout frame is off.
windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
// 默认mCompatFrame等于mFrame
windowFrames.mCompatFrame.set(windowFrames.mFrame);
// 只有兼容模式或者窗口缩放情况下mCompatFrame需要乘以缩放比例
if (hasCompatScale()) {
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
......
// 设置mRealFrame,默认等于mFrame
// Calculate relative frame
windowFrames.mRelFrame.set(windowFrames.mFrame);
WindowContainer parent = getParent();
int parentLeft = 0;
int parentTop = 0;
if (mIsChildWindow) {
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) {
final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
windowFrames.mFrame.top - parentTop);
}
step4.1 applyGravityAndUpdateFrame()
private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
Rect displayFrame) {
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
// We need to fit it to the display if either
// a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
// for the taskless windows)
// b) If it's a secondary app window, we also need to fit it to the display unless
// FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
// screen, but SurfaceViews want to be always at a specific location so we don't fit it to
// the display.
final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
|| ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
float x, y;
int w,h;
final boolean hasCompatScale = hasCompatScale();
// 计算宽高和坐标
if ((mAttrs.flags & FLAG_SCALED) != 0) {
if (mAttrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
w = (int)(mAttrs.width * mGlobalScale + .5f);
} else {
w = mAttrs.width;
}
if (mAttrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
h = (int)(mAttrs.height * mGlobalScale + .5f);
} else {
h = mAttrs.height;
}
} else {
if (mAttrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
w = (int)(mRequestedWidth * mGlobalScale + .5f);
} else {
w = mRequestedWidth;
}
if (mAttrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
h = (int)(mRequestedHeight * mGlobalScale + .5f);
} else {
h = mRequestedHeight;
}
}
if (hasCompatScale) {
x = mAttrs.x * mGlobalScale;
y = mAttrs.y * mGlobalScale;
} else {
x = mAttrs.x;
y = mAttrs.y;
}
......
}
boolean hasCompatScale() {
return (mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord)) && !mIsChildWindow;
}
/**
* @return {@code true} if the application runs in size compatibility mode.
* @see android.content.res.CompatibilityInfo#supportsScreen
* @see ActivityRecord#hasSizeCompatBounds()
*/
static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
|| (windowToken != null && windowToken.hasSizeCompatBounds()
// Exclude starting window because it is not displayed by the application.
&& attrs.type != TYPE_APPLICATION_STARTING);
}
本方法主要是处理兼容模式或者自定义窗口缩放情况下width和height和坐标的校正。hasCompatScale就是表示兼容模式或者自定义窗口缩放。startingWindow在androidT上进行了兼容,S上显示有问题。