- @param client IWindow对象,代表客户端的Window,用于WMS向ViewRootImpl发起交互
- @param seq 请求序列
- @param attrs 窗口各种参数和属性
- @param requestedWidth 客户端请求窗口的宽
- @param requestedHeight 客户端请求窗口的高
- @param viewVisibility View的可见性
- @param flags 标记
- @param frameNumber
- @param outFrame 返回给ViewRootImpl的窗口框架
- @param outContentInsets
- @param outVisibleInsets
- @param outStableInsets
- @param outBackdropFrame
- @param outCutout
- @param mergedConfiguration
- @param outSurfaceControl 返回给ViewRootImpl的Surface管理对象
- @param outInsetsState
- @param outActiveControls
- @param outSurfaceSize
- @param outBLASTSurfaceControl
- @return
*/
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
…
boolean configChanged;
synchronized (mGlobalLock) {
// 获取WindowState、DisplayContent、DisplayPolicy、WindowStateAnimator对象
final WindowState win = windowForClientLocked(session, client, false);
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
// 给WindowState设置来自应用请求的窗口大小
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
// 设置Frame number
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为主体进行各种操作。这个方法很长,但主要有以下几个步骤:
- WindowState#setRequestedSize()设置WindowState宽高;
- DisplayPolicy#adjustWindowParamsLw()调整窗口属性;
- WindowState#setWindowScale()设置缩放比例;
- WindowState#setViewVisibility()更新可见性属性;
- WindowState#relayoutVisibleWindow()处理FLAG_TURN_SCREEN_ON标记;
- WMS#createSurfaceControl()创建Surface;
- WMS#updateFocusedWindowLocked()更新焦点窗口;
- 设置MergedConfiguration对象返回客户端;
- 设置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 != requestedWidth) ?
(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;
}
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
没有Surface
w.setHasSurface(true);
} catch (OutOfResourcesException e) {
}
mLastHidden = true;
return mSurfaceController;
}
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-9B90bWUT-1718820088229)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取