Android R WindowManagerService模块(3) Window的relayout过程,灵魂拷问

win.setFrameNumber(frameNumber);

final DisplayContent dc = win.getDisplayContent();
// 如果此时没有执行Configuration的更新,试图结束衔接动画
if (!dc.mWaitingForConfig) {
win.finishSeamlessRotation(false /* timeout */);
}
// 用来标记属性是否发生变化
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
int privflagChanges = 0;
if (attrs != null) {
// 调整特殊类型的Window#attrs属性
displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
// 针对壁纸窗口调整Window#attrs属性
win.mToken.adjustWindowParams(win, attrs);
// 调整mSystemUiVisibility属性,控制status bar的显示
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
| attrs.subtreeSystemUiVisibility;

}
win.mSystemUiVisibility = systemUiVisibility;
}

// PRIVATE_FLAG_PRESERVE_GEOMETRY将忽略新x、y、width、height值,使用旧值
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
!= 0) {
attrs.x = win.mAttrs.x;
attrs.y = win.mAttrs.y;
attrs.width = win.mAttrs.width;
attrs.height = win.mAttrs.height;
}
// 确定flag是否发生变化
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
// 根据flag是否发生变化做出对应响应,略…

}

// 根据应用请求设置宽高,获取窗口缩放比例
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
// 窗口此时的可见状态
final int oldVisibility = win.mViewVisibility;
// 窗口是否要由不可见状态转变为可见状态
final boolean becameVisible =
(oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
&& viewVisibility == View.VISIBLE;
// 是否需要移除IME窗口
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
// 确定是否需要更新focus状态,第一次执行relayout时,mRelayoutCalled为false
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
// 如果窗口可见性发生变化,且该窗口允许显示在壁纸之上,则对壁纸窗口进行处理
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;

win.mRelayoutCalled = true; // 设置WindowState#mRelayoutCalled为true
win.mInRelayout = true; // 设置WindowState#mInRelayout为true,表示在relayout过程中,relayout完毕后,重置为false
// 更新窗口的可见性
win.setViewVisibility(viewVisibility);
// 通知DisplayContent,需要进行重新布局
win.setDisplayLayoutNeeded();
// 表示是否在等待设置Inset
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

// 如果窗口可见,进行重新布局
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
// 执行一遍刷新操作
mWindowPlacerLocked.performSurfacePlacement(true /* force */);

// 是否需要进行relayout操作
if (shouldRelayout) {
// 布局
result = win.relayoutVisibleWindow(result, attrChanges);
try {
// 创建Surface
result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
result, win, winAnimator);
} catch (Exception e) {
return 0;
}
// 如果是第一次relayout操作,需要focusMayChange
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;
}
} else {

}
// 更新FocusWindow
if (focusMayChange) {
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /updateInputWindows/)) {
imMayMove = false;
}
}
// 是否首次更新
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
// 更新屏幕显示方向
configChanged = displayContent.updateOrientation();
// 对壁纸窗口的特殊处理,更新偏移量
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}

// 更新mergedConfiguration对象
if (shouldRelayout) {
win.getMergedConfiguration(mergedConfiguration);
} else {
win.getLastReportedMergedConfiguration(mergedConfiguration);
}
// 设置各种Inset和DisplayCutout
win.getCompatFrame(outFrame);
win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
outStableInsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
outInsetsState.set(win.getInsetsState(), win.isClientLocal());

// 更新relayout标记,表示可接受touch事件
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;

win.mInRelayout = false; // 重置为false,表示relayout过程完成
// configuration发生变化时,更新全局Configuration
if (configChanged) {
displayContent.sendNewConfiguration();
}
// 设置outSurfaceSize
if (winAnimator.mSurfaceController != null) {
outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
winAnimator.mSurfaceController.getHeight());
}
getInsetsSourceControls(win, outActiveControls);
}

return result;
}

在进入这个方法后,首先通过IWindow对象获取了对应的WindowState对象,之后将以WindowState为主体进行各种操作。这个方法很长,但主要有以下几个步骤:

  1. WindowState#setRequestedSize()设置WindowState宽高;
  2. DisplayPolicy#adjustWindowParamsLw()调整窗口属性;
  3. WindowState#setWindowScale()设置缩放比例;
  4. WindowState#setViewVisibility()更新可见性属性;
  5. WindowState#relayoutVisibleWindow()处理FLAG_TURN_SCREEN_ON标记;
  6. WMS#createSurfaceControl()创建Surface;
  7. WMS#updateFocusedWindowLocked()更新焦点窗口;
  8. 设置MergedConfiguration对象返回客户端;
  9. 设置windowFrame和Inset返回客户端;

下面对以上每个步骤进行分析总结。

1.WindowState#setRequestedSize()设置WindowState宽高

通过WindowState#setRequestedSize()方法,设置WindowState对象mRequestedWidth和mRequestedHeight属性,这两属性表示来自应用请求设置的宽高:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

void setRequestedSize(int requestedWidth, int requestedHeight) {
if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
mLayoutNeeded = true;
mRequestedWidth = requestedWidth;
mRequestedHeight = requestedHeight;
}
}

2.DisplayPolicy#adjustWindowParamsLw()调整窗口属性

接下来,会根据系统当前条件,对客户端传入的布局参数进行调整,主要通过DisplayPolicy#adjustWindowParamsLw(win, attrs, pid, uid)调整,对于壁纸这类特殊窗口,会通过WallpaperWindowToken#adjustWindowParams()方法进行调整。

如果调整后的布局参数发生了变化,则进行相关状态的处理。这部分代码略过。

3.WindowState#setWindowScale()设置缩放比例

通过WindowState#setWindowScale()方法,设置WindowState对象mHScale和mVScale属性,这两属性表示窗口在横竖方向的缩放比例:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

void setWindowScale(int requestedWidth, int requestedHeight) {
final boolean scaledWindow = (mAttrs.flags & FLAG_SCALED) != 0;
if (scaledWindow) {
mHScale = (mAttrs.width != reque

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

stedWidth) ?
(mAttrs.width / (fl性oat)requestedWidth) : 1.0f;
mVScale = (mAttrs.height != requestedHeight) ?
(mAttrs.height / (float)requestedHeight) : 1.0f;
} else {
mHScale = mVScale = 1;
}
}

可以看到,只有对在布局参数中设置了FLAG_SCALED标记的窗口,才会计算缩放比例,否则都是1。

4.WindowState#setViewVisibility()更新可见性

通过WindowState#setViewVisibility()方法,设置WindowState对象mViewVisibility属性,该属性表示该Window的可见性:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

void setViewVisibility(int viewVisibility) {
mViewVisibility = viewVisibility;

}

mViewVisibility有三个值,均来自于APP端的View:

  • View.VISIBLE:表示该窗口可见;
  • View.INVISIBLE:表示该窗口不可见,但仍然占据空间,其Surface依然存在;
  • View.GONE:表示该窗口不可见,并且不再占据空间,其Surface也被销毁。

5.WindowState#relayoutVisibleWindow()处理FLAG_TURN_SCREEN_ON

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

int relayoutVisibleWindow(int result, int attrChanges) {
// 是否可见
final boolean wasVisible = isVisibleLw();
// 如果不可见,表示第一次进行relayout
result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
// 是否执行退出动画
if (mAnimatingExit) {

}
// 是否在销毁Surface列表中,等待被Destory
if (mDestroying) {
mDestroying = false;
mWmService.mDestroySurface.remove(this);
}

if (!wasVisible) {
// 表示即将执行进入动画
mWinAnimator.mEnterAnimationPending = true;
}
// 最近一次可见时的屏幕方向
mLastVisibleLayoutRotation = getDisplayContent().getRotation();
// 表示开始执行进入动画
mWinAnimator.mEnteringAnimation = true;
try {
prepareWindowToDisplayDuringRelayout(wasVisible);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// 当布局参数中像素格式发生变化后的处理
if ((attrChanges & FORMAT_CHANGED) != 0) {

}
// 当拖拽发生resize时
if (isDragResizeChanged()) {

}

return result;
}

在这个方法中,调用prepareWindowToDisplayDuringRelayout()方法,针对亮屏flag进行处理:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// 是否带有点亮屏幕的标记
final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
|| (mActivityRecord != null && mActivityRecord.canTurnScreenOn());
if (hasTurnScreenOnFlag) {

if (allowTheaterMode && canTurnScreenOn
&& (mWmService.mAtmInternal.isDreaming()
|| !mPowerManagerWrapper.isInteractive())) {

mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, “android.server.wm:SCREEN_ON_FLAG”);
}

if (mActivityRecord != null) {
mActivityRecord.setCurrentLaunchCanTurnScreenOn(false);
}
}

if (wasVisible) {
return;
}

}

以上方法中,如果布局参数或对应的ActivityRecord中携带有亮屏相关flag,则进行亮屏。窗口中通过FLAG_TURN_SCREEN_ON进行亮屏,就是在这里触发的。

6.WMS#createSurfaceControl()创建Surface

在客户端ViewRootImpl中,有一个Surface对象,它是这个ViewRootImpl中View显示内容的最终呈现者。但它在创建时,仅仅是一个空壳,没有任何内容。在relayout()时,ViewRootImpl将SurfaceControl对象传递给了WMS,WMS中会创建一个SurfaceControl对象,并将它复制给传入的SurfaceControl,最终ViewRootImpl从SurfaceControl中获取内容:

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

private int createSurfaceControl(SurfaceControl outSurfaceControl,
SurfaceControl outBLASTSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
// 此时该WindowState还没有对应Surface
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}

WindowSurfaceController surfaceController;
try {
// 创建一个WindowSurfaceController对象
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
} finally {
}
if (surfaceController != null) {
// 将WindowSurfaceController中的mSurfaceControl复制给outSurfaceControl
surfaceController.getSurfaceControl(outSurfaceControl);
surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
}

return result;
}

以上方法中:

  • 首先给result变量设置RELAYOUT_RES_SURFACE_CHANGED标记,表示relayout过程的第二个阶段;
  • 然后通过WindowStateAnimator#createSurfaceLocked()创建了WindowSurfaceController对象;
  • 最后通过WindowSurfaceController将内部SurfaceControl拷贝给了outSurfaceControl,outSurfaceControl将返回给客户端的ViewRootImpl。

WindowStateAnimator对象的作用是执行和管理WindowState动画和Surface操作,而WindowSurfaceController就是为WindowStateAnimator对象管理SurfaceControl对象。

创建WindowSurfaceController对象如下:

// frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java

WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
final WindowState w = mWin;
// 如果已经存在,直接返回
if (mSurfaceController != null) {
return mSurfaceController;
}
// 设置WindowState#mHasSurface属性为false,表示没有Surface
w.setHasSurface(false);
// 重置绘制状态
resetDrawState();
//
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
// 计算Surface区域大小,relayout时,由于没有填充Frame,因此获取区域为0
calculateSurfaceBounds(w, attrs, mTmpSize);

try {

// 创建WindowSurfaceController对象
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
height, format, flags, this, windowType, ownerUid);
mSurfaceFormat = format;
// 设置WindowState#mHasSurface属性为false,表示没有Surface
w.setHasSurface(true);
} catch (OutOfResourcesException e) {
}
mLastHidden = true;

return mSurfaceController;
}

在创建WindowSurfaceController对象时,将会创建对应的SurfaceControl对象:

// frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java

WindowSurfaceController(String name, int w, int h, int format,
int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator; // WindowStateAnimator对象
mSurfaceW = w; // Surface的宽,创建时为1, 在setBufferSizeInTransaction()中会设置具体值
mSurfaceH = h; // Surface的高,创建时为1
title = name; // 窗口名

你可能感兴趣的:(程序员,面试,android,移动开发)