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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值