前两篇文章中,对窗口的添加流程进行了分析,这篇文章中对窗口的定位流程进行分析总结。
首先需要说的是WindowSurfacePlacer类,在WMS启动的时候,就创建了WindowSurfacePlacer对象,该对象专门用来执行窗口Surface的“摆放”操作。
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
......
// 创建WindowSurfacePlacer对象
mWindowPlacerLocked = new WindowSurfacePlacer(this);
......
}
WindowSurfacePlacer#performSurfacePlacement()
方法,可以说是WMS中最核心的方法,负责所有窗口的Surface的摆放工作,如何显示、显示在什么位置、显示区域多大,都将通过这个方法,完成确认并下发给SurfaceFlinger中进行显示。当WMS中任何状态发生变化,都会触发该方法的执行,对整个WMS树结构进行遍历,确定所有的Surface的可见与否。下面就从该方法开始,详细分析下整个流程。
1.WindowSurfacePlacer#performSurfacePlacement()
当WMS中某些状态发生变化后,都会通过该方法进行窗口摆放,该方法中会调用重载方法继续执行:
// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
其中,mDeferDepth
表示延迟进行布局的计数器,mDeferredRequests
表示在延迟布局时,请求进行Surface Placement的次数,这两个值都是为了优化性能而添加,当系统状态发生了多个变化时,会延迟摆放过程,直到变化完成后一次性进行。
这里还设置了一个循环体,遍历执行内部的 performSurfacePlacementLoop()
方法,最多执行6次。
mTraversalScheduled表示是否有收到执行遍历的请求,它只有在一个地方设置为true:
// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
void requestTraversal() {
// 设置为true
mTraversalScheduled = true;
if (mDeferDepth > 0) {
mDeferredRequests++;
return;
}
// 在andriod.animation线程进行
mService.mAnimationHandler.post(mPerformSurfacePlacement);
}
performSurfacePlacementLoop()如下:
// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
private void performSurfacePlacementLoop() {
// 如果处于窗口摆放过程,直接返回
if (mInLayout) {
return;
}
// 如果defaultDisplay.mWaitingForConfig为true表示DisplayContent在等待更新配置,此时不进行窗口放置,待配置完成后再进行
final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
if (defaultDisplay.mWaitingForConfig) {
return;
}
// 表示在进行窗口摆放
mInLayout = true;
......
try {
// 进入RootWindowContainer中
mService.mRoot.performSurfacePlacement();
// 重置值
mInLayout = false;
// 是否DisplayContent仍然需要进行放置操作
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
} else {
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
} catch (RuntimeException e) {}
}
这个方法中,mInLayout表示是否在进行窗口Surface的摆放工作,这里将会进入RootWindowContainer#performSurfacePlacement()
方法,从WMS树结构的根节点开始,开始对WMS树进行遍历。
2.RootWindowContainer#performSurfacePlacementNoTrace()中原子操作进行定位
在这个方法中,在Transaction对象上进行原子操作,进行所有窗口的定位:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacementNoTrace() {
.......
// 开启事务
mWmService.openSurfaceTransaction();
try {
// 执行原子操作
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
} finally {
// 提交事务
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
}
........
}
这个方法中:
-
- 首先,通过WMS#openSurfaceTransaction()方法,获取一个SurfaceControl#sGlobalTransaction对象;
-
- 然后执行applySurfaceChangesTransaction()进行所有SurfaceControl的遍历;
-
- 最后通过WMS#closeSurfaceTransaction()方法,对sGlobalTransaction上的一系列SurfaceControl进行提交,发送给SurfaceFlinger。整个过程中,所有SurfaceControl的原子操作就是通过sGlobalTransaction来进行。
我们接着看applySurfaceChangesTransaction()方法,这里会每个DisplayContent进行遍历:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private void applySurfaceChangesTransaction() {
mHoldScreenWindow = null;
mObscuringWindow = null;
final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked(); // 默认DisplayContent
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo(); // 默认DisplayContent对应逻辑屏信息
final int defaultDw = defaultInfo.logicalWidth; //逻辑屏宽高
final int defaultDh = defaultInfo.logicalHeight;
......
//遍历每个DisplayContent,对每个Display中的Surface进行Placement操作
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
// 通知DMS进行Display侧的属性调整
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
// 将mDisplayTransaction合并到sGlobalTransaction上
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
这个方法中,通过mDisplayTransaction对象对几个特殊SurfaceControl进行了操作。mDisplayTransaction是一个单独的事务对象,专门用于对Display相关的原子操作,当完成DisplayContent#applySurfaceChangesTransaction()后,会合并到全局Transaction对象中。
3.DisplayContent#applySurfaceChangesTransaction()
接下来会遍历DisplayContent,执行每个DisplayContent的applySurfaceChangesTransaction()方法,DisplayContent对象和逻辑屏是一对一关系,代表一个逻辑屏的显示容器。
DisplayContent#applySurfaceChangesTransaction()方法如下:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
int repeats = 0;
do {
......
// 1.进行layout操作
if (repeats < LAYOUT_REPEAT_THRESHOLD) {
performLayout(repeats == 1, false /* updateInputWindows */);
}
pendingLayoutChanges = 0;
// 2.进行Display policy和Window policy的应用
try {
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
} finally {
}
mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
try {
// 3. 进行WindowState的状态的转变
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
}
// 4.做最后的准备工作
prepareSurfaces();
........
}
该方法中,有3个步骤:
- Step1. 执行performLayout()方法,进行layout操作。
各个WindowState的WindowFrame由此步进行确认;
- Step2. 执行layout的后期操作,应用Display和Window相关的Policy设置;
对各个WindowState应用当前的一些策略,并策略用于控制窗口的显示和隐藏状态中去。
- Step3. 执行所有窗口的更新和状态的转变;
遍历各个WindowState,根据携带参数确定是否修改逻辑屏显示参数、对WindowState状态进行由COMMIT_DRAW_PENDING到READY_TO_SHOW的转变;
Step4.准备好Surface等待事物的提交。
对每个Surface的位置、大小等做最后的准备,并提交给sGlobalTransation对象等待进行show或hide。
3.1.performLayout() 确定DisplayFrame和WindowFrame
通过这一步,会根据当前一些系统窗口,如状态栏、Navigation Bar等更新DisplayFrame,并对所有窗口的WindowFrame进行确定。performLayout()方法直接调用了performLayoutNoTrace()方法:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
// 如果mLayoutNeeded为false, 直接返回
if (!isLayoutNeeded()) {
return;
}
.....
// DisplayPolicy#beginLayoutLw()进行布局的前期工作,将重置一些状态
mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
// 进行非子窗口的layout
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// 进行子窗口的layout
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
......
}
可以看到,DisplayContent#mLayoutNeeded属性作为控制该操作是否执行的关键。这个方法中也分三个步骤:
- 执行DisplayPolicy#beginLayoutLw()方法,这个方法中会对status bar和nav bar进行布局,并根据status bar和nav bar的状态,更新DisplayFrame中的各个Rect属性,完成对DisplayFrame的确定;
- 对所有WindowState进行layout操作,mPerformLayout是一个函数接口Consumer的实例,通过forAllWindows()完成所有WindowState的遍历,完成对WindowFrame的确定;
- 对非TYPE_APPLICATION_ATTACHED_DIALOG类型的依附窗口进行额外的处理,WindowState#mLayoutAttached属性表示依附窗口,即属于SubWindow类型的窗口。
接下来,主要对第二步进行主要分析,mPerformLayout如下:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mPerformLayout = w -> {
// 判断是否可以进行layout
final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
|| w.isGoneForLayoutLw();
// 更新WindowState#mBehindIme属性
if (w.mBehindIme != mTmpWindowsBehindIme) {
w.mBehindIme = mTmpWindowsBehindIme;
....
}
// 遍历到InputMethodWindow时,将mTmpWindowsBehindIme设置为true,此后的WindowState的mBehindIme属性将被设置为true
if (w == mInputMethodWindow) {
mTmpWindowsBehindIme = true;
}
// 当WindowState不可见且已经有frame时,不会进行layout
if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
// 重置参数
w.mLayoutNeeded = false;
// 是否是第一次进行layout, 根据WindowState#mLayoutSeq属性确定
final boolean firstLayout = !w.isLaidOut();
// 设置WindowFrame各个Rect属性
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
......
}
};
在这个方法中,InputMethodWindow之后的WindowState,其mBehindIme
属性将被设置为true,表示位于InputMethodWindow窗口下面。
然后,当WindowState可见时,执行DisplayPolicy#layoutWindowLw()方法,这里将会根据DisplayFrame对象,对WindowFrame中的各个Rect对象属性进行确定。确定WindowFrame的过程很长,这里就不进行分析了。
经过这一步后,每个WindowState的WindowFrame就确定了下来,也就意味这窗口的大小计算完成。
3.2.mApplyPostLayoutPolicy对WindowState进行policy应用
接下来是WindowState对policy的应用,这一操作也分三步进行:
- DisplayPolicy.beginPostLayoutPolicyLw()对DisplayPolicy中相关参数进行重置;
- 函数接口mApplyPostLayoutPolicy对每个WindowState进行policy应用;
- DisplayPolicy.finishPostLayoutPolicyLw()进行完成policy应用后的操作;
这里也只对最关键的一部分——WindowState对policy的应用——进行分析,这一步通过函数接口对象mApplyPostLayoutPolicy
进行,mApplyPostLayoutPolicy收到回调后,将直接调用DisplayPolicy#applyPostLayoutPolicyLw()
方法,在applyPostLayoutPolicyLw()中,最终通过WindowManagerPolicy#applyKeyguardPolicyLw()对keyguard相关的policy进行了应用:
// frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
......
// 对win应用Policy
mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget);
......
}
applyKeyguardPolicyLw()方法中,将根据当前是否锁屏和WindowState是否能在锁屏上显示,对WindowState设置相应的可见标记:
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
// 该win是否能被keyguard隐藏
if (canBeHiddenByKeyguardLw(win)) {
// 是否当前应该进行隐藏
if (shouldBeHiddenByKeyguard(win, imeTarget)) {
win.hideLw(false);
} else {
win.showLw(false);
}
}
}
首先会判断当前WindowState是否能够被keyguard隐藏,这将直接影响到窗口的显示。如果能够被隐藏,则接下来会判断是否应该进行隐藏,如果需要进行隐藏,则调用WindowState#hideLw()进行隐藏,否则调用WindowState#showLw()进行显示。下面依次看下这个过程。
1.WindowState是否能够被keyguard隐藏
先来看如何确认WindowState是否能被Keyguard隐藏:
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public boolean canBeHiddenByKeyguardLw(WindowState win) {
// 如果是Activity类型的WindowState,是否可见由ActivityRecord决定,不会被keyguard覆盖
if (win.getAppToken() != null) {
return false;
}
// 以下四个窗口类型,不会被Keyguard覆盖
switch (win.getAttrs().type) {
case TYPE_NOTIFICATION_SHADE:
case TYPE_STATUS_BAR:
case TYPE_NAVIGATION_BAR:
case TYPE_WALLPAPER:
return false;
default:
// 当前Win的layer是否小于
return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
}
}
以上流程说明,对于Activity窗口、四类系统窗口、以及窗口类型对应的Layer值大于TYPE_NOTIFICATION_SHADE类型窗口的Layer值的窗口,Keyguard是不会进行覆盖,除此之外的窗口,都会进行覆盖。
2.当前WindowState是否应该被覆盖
如果能够被覆盖,接下来会确认是否应该被覆盖:
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) {
final LayoutParams attrs = win.getAttrs();
......
// 如果当前窗口为IME窗口,且处于AOD状态,则对IME窗口进行隐藏
final boolean hideIme = win.isInputMethodWindow()
&& (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
if (hideIme) {
return true;
}
//如果当前IME Target窗口存在且可见,且该IME Target能在锁屏上显示
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw()
&& (imeTarget.canShowWhenLocked() || !canBeHiddenByKeyguardLw(imeTarget));
// 锁屏时显示IME窗口
boolean allowWhenLocked = win.isInputMethodWindow() && showImeOverKeyguard;
// 判断是否处于锁屏状态
final boolean isKeyguardShowing = mKeyguardDelegate.isShowing();
// 如果处于锁屏状态但锁屏被遮挡
if (isKeyguardShowing && isKeyguardOccluded()) {
allowWhenLocked |= win.canShowWhenLocked()
|| (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
}
// 返回"当前是否处于锁屏状态且不允许在锁屏上显示"
return isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY;
}
这个方法中,如果确定当前系统处于锁屏状态,且当前WindowState不允许在锁屏上显示,那么就说明需要被隐藏。
3.WindowState的隐藏和显示
如果满足条件,则通过hideLw()和showLw()方法,对WindowState进行隐藏操作,这里直接通过对WindowState#mPolicyVisibility
属性设置标记来实现:
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
boolean hideLw(boolean doAnimation, boolean requestAnim) {
......
if (!doAnimation) {
// 清除LEGACY_POLICY_VISIBILITY标记
clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
}
return true;
}
boolean showLw(boolean doAnimation, boolean requestAnim) {
// 添加LEGACY_POLICY_VISIBILITY标记
setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
return true;
}
WindowState最终的显示与否,取决于许多影响其可见的变量。这里通过policy的方式,无论客户端和WMS状态是否让其显示,都会对窗口进行隐藏。
3.3.mApplySurfaceChangesTransaction
mApplyPostLayoutPolicy接口执行完毕后,接下来执行mApplySurfaceChangesTransaction
函数接口,在该接口中,会遍历各个WindowState,根据携带参数确定是否修改逻辑屏显示参数,并且对WindowState状态进行由COMMIT_DRAW_PENDING
到READY_TO_SHOW
的转变:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
// 确定当前WindowState是否被上面的WindowState遮挡
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
// 当前没有遮挡其他窗口的WindowState,会获取win中设置的逻辑屏显示相关属性
if (!mTmpApplySurfaceChangesTransactionState.obscured) {
final boolean isDisplayed = w.isDisplayedLw();
// 确定是否会遮挡之后进行遍历的WindowState
if (isDisplayed && w.isObscuringDisplay()) {
root.mObscuringWindow = w;
mTmpApplySurfaceChangesTransactionState.obscured = true;
}
// 逻辑屏上是否有内容显示,用于控制多屏镜像
final boolean displayHasContent = root.handleNotObscuredLocked(w,
mTmpApplySurfaceChangesTransactionState.obscured,
mTmpApplySurfaceChangesTransactionState.syswin);
if (!mTmpApplySurfaceChangesTransactionState.displayHasContent
&& !getDisplayPolicy().isWindowExcludedFromContent(w)) {
mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent;
}
// 确定WindowState是否携带了preferredRefreshRate、preferredDisplayModeId等属性
if (w.mHasSurface && isDisplayed) {
final int type = w.mAttrs.type;
......
if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0
&& w.mAttrs.preferredRefreshRate != 0) {
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
= w.mAttrs.preferredRefreshRate;
}
......
}
}
// Surface状态的转换
if (w.mHasSurface) {
final boolean committed = winAnimator.commitFinishDrawingLocked();
......
}
......
};
这个方法中,主要做了三件事:
- 设置WindowState#mObscured属性值;
- 将窗口所携带控制逻辑屏显示相关参数填充给mTmpApplySurfaceChangesTransactionState对象属性;
- 对已经画好的窗口进行commit操作。
1.设置WindowState#mObscured属性
WindowState#mObscured表示该窗口是否被其他窗口遮挡。在此次遍历过程中,只要一个窗口已经显示且全屏显示,那么之后遍历的WindowState的mObscured属性将设置为true,表示将会被遮挡。同时将mTmpApplySurfaceChangesTransactionState.obscured设置为true。
mTmpApplySurfaceChangesTransactionState对象用来在遍历过程中保存对所有窗口遍历的结果,之所以以"temp"命名,是因为每次进行窗口遍历时,都会重置该对象。
2.填充mTmpApplySurfaceChangesTransactionState对象
在确定完WindowState#mObscured属性后,如果没有存在遮挡其他WindowState的窗口,接下来将会获取窗口属性中携带的用于控制逻辑屏显示参数的一些属性,主要有:
- w.mAttrs.preferredRefreshRate、w.mAttrs.preferredDisplayModeId携带的刷新率;
- w.mAttrs.screenBrightness携带的窗口亮度;
- w.mAttrs.userActivityTimeout携带的自动休眠时间;
同时,会通过RootWindowContainer#handleNotObscuredLocked()方法,确定displayHasContent变量,表示对应窗口是否需要镜像给其他display:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
......
if (w.mHasSurface && canBeSeen) {
......
final int type = attrs.type;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && displayContent.isDefaultDisplay) {
// 对于Dream类型窗口或锁屏显示时,副屏上需要不进行显示
if (w.isDreamWindow() || mWmService.mPolicy.isKeyguardShowing()) {
mObscureApplicationContentOnSecondaryDisplays = true;
}
// 默认屏幕永远为true
displayHasContent = true;
} else if (displayContent != null &&
(!mObscureApplicationContentOnSecondaryDisplays
|| (obscured && type == TYPE_KEYGUARD_DIALOG))) {
// 满足该条件的窗口在副屏上也会进行显示
displayHasContent = true;
}
......
}
return displayHasContent;
}
最终遍历完成后,将值保存在mTmpApplySurfaceChangesTransactionState
中,并在最后向DMS发出请求,为顶层window设置显示参数,如刷新率、宽高、是否镜像......
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
mTmpApplySurfaceChangesTransactionState.reset();
......
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
}
3. 对绘制完成的SurfaceControl进行提交
最后一个操作是执行WindowStateAnimator#commitFinishDrawingLocked()
方法,通过这个方法,会将已经绘制完成的窗口进行提交:
// frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean commitFinishDrawingLocked() {
......
// 绘制状态变为READY_TO_SHOW
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
// 如果该WindowState的WindowToken管理的所有WindowState都完成绘制,则直接进行show操作
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
经过这个方法,窗口的绘制状态将由COMMIT_DRAW_PENDING
变为READY_TO_SHOW
状态,进入这个状态,表示窗口已经绘制完成并且完成提交,接下来将等待同一WindowToken的窗口完成绘制后,进行最终的show操作。
窗口的显示过程共有五个状态:
- NO_SURFACE:在创建WindowState后的默认状态,表示当前窗口还创没有执行relayout()方法创建Surface;
- DRAW_PENDING:执行relayout()方法后,创建完成Surface后的状态,表示等待绘制;
- COMMIT_DRAW_PENDING:窗口Surface上完成绘制后的状态,执行WindowStateAnimator#finishDrawingLocked()方法设置,表示已经完成绘制,等待下次刷帧进行提交;
- READY_TO_SHOW:表示窗口已经绘制完成并且完成提交,此时如果该窗口的兄弟窗口全部完成绘制且满足显示要求,则直接进行HAS_DRAWN的转变完成显示,否则等待其他兄弟窗口完成绘制后,再进行HAS_DRAWN转变;
- HAS_DRAWN:表示该窗口正式显示;
至此,mApplySurfaceChangesTransaction中的内容全部完毕。
回到DisplayContent#app()方法中,接下来将会执行prepareSurface()方法。
3.4.prepareSurfaces()对所有SurfaceControl进行最后的准备
prepareSurfaces()方法将会对每个WindowContainer的Surface做最后的准备工作,各个Surface位置、大小、是否显示在屏幕上等,都将通过这个方法来进行设置,并在关闭事务后,提交给SurfaceFlinger进行显示。
相比前面几个函数接口都是针对WindowState进行遍历,该方法则针对所有的WindowContainer进行遍历,遍历方式是由父容器→ 子容器进行,遍历路径如下图:
黄色加深的都是实现了WindowContainer#prepareSurfaces()方法的类,下面就按照次序,看下几个重要类中的prepareSurfaces()方法。
1.DisplayContent#prepareSurfaces()
在DisplayContent#prepareSurfaces()中,主要作用是获取一个Transaction对象mPendingTransaction,并继续遍历子容器:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void prepareSurfaces() {
try {
// 获取mPendingTransaction
final Transaction transaction = getPendingTransaction();
// 执行WindowContainer#prepareSurfaces()
super.prepareSurfaces();
// 将mPendingTransaction合并到sGlobalTransaction
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
}
}
后面可以看到,所有的prepareSurfaces()都是将SurfaceControl提交在了mPendingTransaction上。然后完成遍历后,将mPendingTransaction合并到全局Transaction对象上提交给SurfaceFlinger。
2.WindowContainer#prepareSurfaces()
在WindowContainer中,主要是提供了统一的遍历子容器的逻辑:
// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void prepareSurfaces() {
......
// 遍历子容器
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
}
}
因此,接下来DisplayContent的子容器的对应方法将会执行,依次类推......下面我们着重看下ActivityRecord和WindowState中的实现
3.ActivityRecord#prepareSurfaces()
在ActivityRecord中,将会根据其mVisible属性,对它的mSurfaceControl进行show或hide操作,同时遍历子容器prepareSurfaces操作:
// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void prepareSurfaces() {
// 该ActivityRecord管理的Window是否要可见
final boolean show = isVisible() || isAnimatingExcluding(PARENTS,
ANIMATION_TYPE_SCREEN_ROTATION);
if (mSurfaceControl != null) {
// 对该SurfaceControl进行show
if (show && !mLastSurfaceShowing) {
getSyncTransaction().show(mSurfaceControl);
} else if (!show && mLastSurfaceShowing) {
// 对该SurfaceControl进行show
getSyncTransaction().hide(mSurfaceControl);
}
}
......
// 表示最近一次prepareSurfaces操作
mLastSurfaceShowing = show;
super.prepareSurfaces(); //继续遍历子容器
}
在这个方法中,通过getSyncTransaction()方法得到Transaction对象,并在该Transaction上对该容器的mSurfaceControl进行show或hide的操作。这个Transaction对象其实就是DisplayContent#mPendingTransaction对象。
对于所有DisplayContent以下的WindowContainer来说,它的mSurfaceControl对象并非是直接进行内容显示的Surface载体,真正的载体是在relayout()过程中由WindowSurfaceController管理并创建的对于由ActivityRecord管理的mSurfaceControl对象,而且WindowContainer#mSurfaceControl作为WindowSurfaceController#mSurfaceControl的父SurfaceControl。对于SurfaceFlinger来说,WindowContainer#mSurfaceControl是一个ContainerLayer,WindowSurfaceController#mSurfaceControl才真正进行绘制的BufferQueueLayer。
这也是为何对于由ActivityRecord来管理的WindowState而言,其可见状态由ActivityRecord控制(canBeHiddenByKeyguardLw()第一个return false)。
4.WindState#prepareSurfaces()
WindowState#prepareSurfaces()方法,是所有prepareSurfaces()方法中最重要的,在这个方法中,将会设置SurfaceControl的位置、Alpha值、Matrix、Crop等各种属性,下面来看这个方法:
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void prepareSurfaces() {
mIsDimming = false;
// 应用Dim Layer
applyDims();
// 更新Surface的位置
updateSurfacePosition();
updateFrameRateSelectionPriorityIfNeeded();
// 进入WindowStateAnimator中进行实际Surface的准备工作
mWinAnimator.prepareSurfaceLocked(true);
notifyBlastSyncTransaction();
super.prepareSurfaces(); // 继续处理子WindowState
}
1.updateSurfacePosition()设置position
首先,通过updateSurfacePosition()设置SurfaceControl的position,这里用的Transaction对象还是mPendingTransaction:
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void updateSurfacePosition(Transaction t) {
// 将WindowState#mWindowFrames转换成Position
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
mSurfacePosition);
if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
&& !mLastSurfacePosition.equals(mSurfacePosition)) {
// 设置position
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
......
}
}
在这个方法中,首先会使用WindowState的mWindowFrames.mFrame获得SurfaceControl的位置坐标点,然后通过Transaction#setPosition()方法完成设置。因此,每个窗口的WindowFrame对象的mFrame属性,就决定了该窗口要具体显示的位置。
2.WindowStateAnimator#prepareSurfaceLocked()
接下来进入到WindowStateAnimator#prepareSurfaceLocked()方法中
// frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
void prepareSurfaceLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
......
boolean displayed = false;
//计算 Alpha和Matrix值
computeShownFrameLocked();
// 计算SurfaceControl的边界、裁剪区域,......
setSurfaceBoundariesLocked(recoveringMemory);
if (mIsWallpaper && !w.mWallpaperVisible) {// 壁纸的特殊处理
......
} else if (mLastAlpha != mShownAlpha
|| mLastDsDx != mDsDx) {
if (mIsWallpaper) {// 壁纸的特殊处理
setWallpaperPositionAndScale(
mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
} else {
// 设置SurfaceControl的alpha和matrix值
prepared =
mSurfaceController.prepareToShowInTransaction(mShownAlpha,
mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
}
// 当mDrawState为HAS_DRAWN时,进行show操作
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
if (showSurfaceRobustlyLocked()) {
markPreservedSurfaceForDestroy();
mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
}
......
}
}
if (hasSurface()) {
w.mToken.hasVisible = true;
}
} else {
displayed = true;
}
......
}
在这个方法中,通过以下四个方法,完成了SurfaceControl最后的准备工作:
- computeShownFrameLocked():计算SurfaceControl的Alpha和Matrix值;
- setSurfaceBoundariesLocked(): 计算并设置SurfaceControl的buffer size、window crop;
- WindowSurfaceController#prepareToShowInTransaction():设置SurfaceControl的Alpha和Matrix值;
- WindowSurfaceController#showSurfaceRobustlyLocked():对SurfaceControl进行显示或隐藏;
这里我们主要看下showSurfaceRobustlyLocked()方法,当mDrawState状态为HAS_DRAWN时,会执行这个方法,并最终执行了WindowSurfaceController#updateVisibility()方法,进行show或hide SurfaceControl:
// frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java
private boolean updateVisibility() {
if (mHiddenForCrop || mHiddenForOtherReasons) {
if (mSurfaceShown) {
// 进行hide操作
hideSurface(mTmpTransaction);
SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
}
return false;
} else {
if (!mSurfaceShown) {
// 进行show操作
return showSurface();
} else {
return true;
}
}
}
当所有WindowContainer#prepareSurfaces()完成后,DisplayContent#applySurfaceChangesTransaction()大部分逻辑执行完毕。
回到RootWindowContainer#performSurfacePlacementNoTrace()方法中,将会通过SurfaceControl.closeTransaction()方法对事务进行提交,此时该Transaction对象上的所有show的SurfaceControl,都会送到SurfaceFlinger中,由SurfaceFlinger进一步处理,进行合成,最终显示在界面上。这个方法中,事务提交之后的流程就暂且略过。
4.总结
以上就是对WindowManagerService中对所有窗口Surface固定工作流程的一些分析,主要还是对事务内的一些逻辑进行了重点分析。在整个过程中,充分利用了各个容器之间的关系和函数接口,从DisplayContent到叶子节点WindowState,进行了完整的遍历,完成了对所有Surface的摆放工作。 整个过程可以分为五个阶段:
- 1.addWindow()执行后,进入NO_SURFACE阶段,表示此时还没有创建Surface;
- 2.relayout()操作,这个过程会创建Surface,进入DRAW_PENDING阶段,并将创建的Surface返回给客户端View;
- 3.View开始进行onMeasure\onDraw()等方法,并在完成后通过Session#finishedDraw交给WMS,此时进入COMMIT_DRAW_PENDING阶段;
- 4.在COMMIT_DRAW_PENDING阶段,将直接过渡到READY_TO_SHOW阶段,表示窗口已经绘制完成并且完成提交,此时如果该窗口的兄弟窗口全部完成绘制且满足显示要求,则直接进行HAS_DRAWN的转变完成显示; 5.当同一个WindowToken的所有窗口完成绘制后,进入HAS_DRAWN阶段,表示该窗口正式显示。
在整个过程中,主要有4个遍历过程,每个遍历中又有不同的流程进行不同的操作,下图中对事务操作中的一些关键点进行了概括(放大看):
整个过程时序图如下: