Window 添加与更新流程
更新 Window
android.view.WindowManagerGlobal#updateViewLayout
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);//1
}
}
注释 1 处更新 ViewRootImpl 的 LayoutParams 属性
android.view.ViewRootImpl#setLayoutParams
public void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
...
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();//1
}
...
if (mWindowAttributes.softInputMode != oldSoftInputMode) {
requestFitSystemWindows();//2
}
mWindowAttributesChanged = true;
scheduleTraversals();//3
}
}
注释 1、注释 2,都会执行 scheduleTraversals 方法(注释 3)
android.view.ViewRootImpl#scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//1
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mChoreographer 用于接收 VSync 信号,在下一帧被渲染时,调用 mTraversalRunnable 的 run 方法
android.view.ViewRootImpl.TraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
android.view.ViewRootImpl#doTraversal
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();//1
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
在注释 1 处开始 View 的绘制流程,即走 view 的 measure、layout、draw 三大步
android.view.ViewRootImpl#performTraversals
private void performTraversals() {
...
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);//1
...
}
在走 view 的 measure、layout、draw 三大步之前,会先调用 relayoutWindow
android.view.ViewRootImpl#relayoutWindow
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
restore = true;
params.backup();
mTranslator.translateWindowLayout(params);
}
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
if (mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Slog.w(mTag, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
}
}
long frameNumber = -1;
if (mSurface.isValid()) {
frameNumber = mSurface.getNextFrameNumber();
}
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);//1
mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
final Surface blastSurface = getOrCreateBLASTSurface();
// If blastSurface == null that means it hasn't changed since the last time we
// called. In this situation, avoid calling transferFrom as we would then
// inc the generation ID and cause EGL resources to be recreated.
if (blastSurface != null) {
mSurface.transferFrom(blastSurface);
}
}
if (mAttachInfo.mThreadedRenderer != null) {
if (HardwareRenderer.isWebViewOverlaysEnabled()) {
addPrepareSurfaceControlForWebviewCallback();
addASurfaceTransactionCallback();
}
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
}
} else {
destroySurface();
}
mPendingAlwaysConsumeSystemBars =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
}
setFrame(mTmpFrames.frame);//2
mWillMove = false;
mWillResize = false;
mInsetsController.onStateChanged(mTempInsets);//3
mInsetsController.onControlsChanged(mTempControls);//4
return relayoutResult;
}
在注释 1 处,mWindowSession 是 IWindowSession 类型的字段,所以这里调用的是 IWindowSession::relayout 方法,IWindowSession 对应服务端的 com.android.server.wm.Session, 一个 ViewRootImpl 对应一个 Session,ViewRootImpl 与 WMS 的交互都要经由 Session 转发
IWindowSession 对应服务端的 com.android.server.wm.Session,所以注释 1 实际执行的是
com.android.server.wm.Session#relayout
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize);//1
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
return res;
}
Session 将 relayoutWindow 转发给 WMS
com.android.server.wm.WindowManagerService#relayoutWindow
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) {
Arrays.fill(outActiveControls, null);
int result = 0;
boolean configChanged;
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
...
// 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.
mWindowPlacerLocked.performSurfacePlacement(true /* force */);//1
...
}
Binder.restoreCallingIdentity(origId);
return result;
}
在注释 1 处,执行 WindowSurfacePlacer 的 performSurfacePlacement 方法,WindowSurfacePlacer 用于定位窗口以及他们的 Surface 的位置
com.android.server.wm.WindowSurfacePlacer#performSurfacePlacement(boolean)
final void performSurfacePlacement(boolean force) {
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();//1
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
com.android.server.wm.WindowSurfacePlacer#performSurfacePlacementLoop
private void performSurfacePlacementLoop() {
...
try {
mService.mRoot.performSurfacePlacement();//1
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
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;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
}
在注释 1 处,mService.mRoot 是 RootWindowContainer 类型
com.android.server.wm.RootWindowContainer#performSurfacePlacement
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
com.android.server.wm.RootWindowContainer#performSurfacePlacementNoTrace
void performSurfacePlacementNoTrace() {
...
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
...
}
com.android.server.wm.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;
if (mWmService.mWatermark != null) {
mWmService.mWatermark.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
if (mWmService.mStrictModeFlash != null) {
mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
if (mWmService.mEmulatorDisplayOverlay != null) {
mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
}
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);//1
dc.applySurfaceChangesTransaction();//2
}
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
注释 1 处的 mChildren 在 WindowContainer 定义如下
// List of children for this window container. List is in z-order as the children appear on
// screen with the top-most window container at the tail of the list.
protected final WindowList<E> mChildren = new WindowList<E>();
WindowContainer 是 AppWindowToken、Task、TaskStack、DisplayContent 的基类,用于管理窗口配置。
显然 mChildren 在这里是 DisplayContent 类型的数组,即注释 2 处调用了
void applySurfaceChangesTransaction() {
...
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
try {
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
"after finishPostLayoutPolicyLw", pendingLayoutChanges);
mInsetsStateController.onPostLayout();
...
}
com.android.server.wm.DisplayPolicy#finishPostLayoutPolicyLw
/**
* Called following layout of all windows and after policy has been applied
* to each window. If in this function you do
* something that may have modified the animation state of another window,
* be sure to return non-zero in order to perform another pass through layout.
*
* @return Return any bit set of
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
*/
public int finishPostLayoutPolicyLw() {
int changes = 0;
boolean topIsFullscreen = false;
// If we are not currently showing a dream then remember the current
// lockscreen state. We will use this to determine whether the dream
// started while the lockscreen was showing and remember this state
// while the dream is showing.
if (!mShowingDream) {
mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
}
if (getStatusBar() != null) {
if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " top=" + mTopFullscreenOpaqueWindowState);
final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags
& PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
boolean topAppHidesStatusBar = topAppHidesStatusBar();
if (mForceStatusBar || forceShowStatusBar) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
// Maintain fullscreen layout until incoming animation is complete.
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
} else if (mTopFullscreenOpaqueWindowState != null) {
topIsFullscreen = topAppHidesStatusBar;
// The subtle difference between the window for mTopFullscreenOpaqueWindowState
// and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
// requests to hide the status bar. Not sure if there is another way that to be the
// case though.
if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
topAppHidesStatusBar = false;
}
}
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
statusBar.setTopAppHidesStatusBar(topAppHidesStatusBar);
}
}
if (mTopIsFullscreen != topIsFullscreen) {
if (!topIsFullscreen) {
// Force another layout when status bar becomes fully shown.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
mTopIsFullscreen = topIsFullscreen;
}
if (updateSystemUiVisibilityLw()) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
if (mShowingDream != mLastShowingDream) {
mLastShowingDream = mShowingDream;
mService.notifyShowingDreamChanged();
}
mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn);
return changes;
}
com.android.server.wm.DisplayPolicy#updateSystemUiVisibilityLw
/**
* @return {@code true} if the update may affect the layout.
*/
boolean updateSystemUiVisibilityLw() {
...
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
...
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
final int displayId = getDisplayId();
statusBar.setDisableFlags(displayId, disableFlags, cause);
statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
isNavbarColorManagedByIme, behavior, isFullscreen);
}
});
..
return true;
}
com.android.server.wm.InsetsPolicy#updateBarControlTarget
void updateBarControlTarget(@Nullable WindowState focusedWin) {
if (mFocusedWin != focusedWin){
abortTransient();
}
mFocusedWin = focusedWin;
boolean forceShowsSystemBarsForWindowingMode = forceShowsSystemBarsForWindowingMode();
InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin,
forceShowsSystemBarsForWindowingMode);
InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin,
forceShowsSystemBarsForWindowingMode || forceShowsNaviBar());
mStateController.onBarControlTargetChanged(statusControlTarget,
getFakeControlTarget(focusedWin, statusControlTarget),
navControlTarget,
getFakeControlTarget(focusedWin, navControlTarget));
mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
}