android S--WMS窗口渲染流程

1,058 阅读4分钟

image.png

RootWindowContainer

    void performSurfacePlacementNoTrace() {
        ......
        final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
        mWmService.openSurfaceTransaction();
        try {
            // step1 处理每个窗口
            applySurfaceChangesTransaction();
        } catch (RuntimeException e) {
           
        } finally {
            mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
        }
        
        checkAppTransitionReady(surfacePlacer);

        // Defer starting the recents animation until the wallpaper has drawn
        final RecentsAnimationController recentsAnimationController =
                mWmService.getRecentsAnimationController();
        if (recentsAnimationController != null) {
            recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
        }

        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
            final DisplayContent displayContent = mChildren.get(displayNdx);
            if (displayContent.mWallpaperMayChange) {
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            }
        }

        if (isLayoutNeeded()) {
            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
            
        }
        // 窗口尺寸变化通知client端
        handleResizingWindows();
        // 清理不可见的窗口,解除冻屏等
        ......

        mWmService.scheduleAnimationLocked();
    }

step1 处理每个窗口 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;
        

        final int count = mChildren.size();
        for (int j = 0; j < count; ++j) {
            final DisplayContent dc = mChildren.get(j);
            // step2 调用DisplayContent#applySurfaceChangesTransaction()
            dc.applySurfaceChangesTransaction();
        }

        // Give the display manager a chance to adjust properties like display rotation if it needs
        // to.
        mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
        SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
    }
step2 调用DisplayContent#applySurfaceChangesTransaction()
void applySurfaceChangesTransaction() {
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;

        mTmpUpdateAllDrawn.clear();

        int repeats = 0;
        do {
            repeats++;
            if (repeats > 6) {
                clearLayoutNeeded();
                break;
            }
            ......
            // FIRST LOOP: Perform a layout, if needed.
            if (repeats < LAYOUT_REPEAT_THRESHOLD) {
                // step 2.1 DisplayContent#performLayout()
                performLayout(repeats == 1, false /* updateInputWindows */);
            } else {
               
            }

            // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
            pendingLayoutChanges = 0;
            try {
                mDisplayPolicy.beginPostLayoutPolicyLw();
                forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
                pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
            } 
            mInsetsStateController.onPostLayout();
        } while (pendingLayoutChanges != 0);

        mTmpApplySurfaceChangesTransactionState.reset();
        try {
            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
        } finally {
        }
        prepareSurfaces();
        ......

    }
step 2.1 DisplayContent#performLayout()
private final Consumer<WindowState> mPerformLayout = w -> {
        // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
        // wasting time and funky changes while a window is animating away.
        final boolean gone = w.isGoneForLayout();

        // If this view is GONE, then skip it -- keep the current frame, and let the caller know
        // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
        // since that means "perform layout as normal, just don't display").
        if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
            if (mTmpInitial) {
                w.resetContentChanged();
            }
            w.mSurfacePlacementNeeded = true;
            w.mLayoutNeeded = false;
            // layout之前的预处理,设置窗口的缩放参数
            w.prelayout();
            final boolean firstLayout = !w.isLaidOut();
            // 计算窗口尺寸 Step3 DisplayPolicy#layoutWindowLw
            getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
            ......
        }
    };



    void performLayout(boolean initial, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
        try {
            performLayoutNoTrace(initial, updateInputWindows);
        } 
    }


    private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
    ......
        // 非子窗口
        // First perform layout of any root windows (not attached to another window).
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

        // 子窗口
        // Now perform layout of attached windows, which usually depend on the position of the
        // window they are attached to. XXX does not deal with windows that are attached to windows
        // that are themselves attached.
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);

       ......
    }
Step3 DisplayPolicy#layoutWindowLw
    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
        ......
        final WindowManager.LayoutParams attrs = win.getAttrs();

        final int type = attrs.type;
        final int fl = attrs.flags;
        final int pfl = attrs.privateFlags;
        final int sim = attrs.softInputMode;

        displayFrames = win.getDisplayFrames(displayFrames);
        final WindowFrames windowFrames = win.getWindowFrames();

        sTmpLastParentFrame.set(windowFrames.mParentFrame);
        // 获取窗口的pf,df
        final Rect pf = windowFrames.mParentFrame;
        final Rect df = windowFrames.mDisplayFrame;
        windowFrames.setParentFrameWasClippedByDisplayCutout(false);

        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
        final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;

        final InsetsState state = win.getInsetsState();
        // 计算窗口尺寸,去掉内衬区域
        computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
        if (attached == null) {
            // 非子窗口 pf等于df
            pf.set(df);
            
        } else {
            // 非子窗口 pf等于父窗口的尺寸
            pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
        }
        ......
        
        // step4 调用windowState#computeFrameAndUpdateSourceFrame计算窗口的真实尺寸
        win.computeFrameAndUpdateSourceFrame();
    }

该方法是窗口尺寸计算的核心,判断是根窗口还是子窗口,去掉内衬区域,然后根据各种frame计算窗口真实尺寸

step4 调用windowState#computeFrameAndUpdateSourceFrame计算窗口的真实尺寸
  void computeFrameAndUpdateSourceFrame() {
        computeFrame();
       ......
    }
    
  void computeFrame() {
        mHaveFrame = true;

        final Task task = getTask();
        final boolean isFullscreenAndFillsArea = !inMultiWindowMode() && matchesDisplayAreaBounds();
        final boolean windowsAreFloating = task != null && task.isFloating();
        final DisplayContent dc = getDisplayContent();
        final DisplayInfo displayInfo = getDisplayInfo();
        final WindowFrames windowFrames = getLayoutingWindowFrames();

        mInsetFrame.set(getBounds());

        // Denotes the actual frame used to calculate the insets and to perform the layout. When
        // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
        // insets temporarily. By the notion of a task having a different layout frame, we can
        // achieve that while still moving the task around.
        final Rect layoutContainingFrame;
        final Rect layoutDisplayFrame;

        // The offset from the layout containing frame to the actual containing frame.
        final int layoutXDiff;
        final int layoutYDiff;
        final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
        final InsetsControlTarget imeTarget = dc.getImeTarget(IME_TARGET_LAYERING);
        final boolean isInputMethodAdjustTarget = windowsAreFloating
                ? imeTarget != null && task == imeTarget.getWindow().getTask()
                : isImeLayeringTarget();
        final boolean isImeTarget =
                imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
        if (isFullscreenAndFillsArea || layoutInParentFrame()) {
            // We use the parent frame as the containing frame for fullscreen and child windows
            windowFrames.mContainingFrame.set(windowFrames.mParentFrame);
            layoutDisplayFrame = windowFrames.mDisplayFrame;
            layoutContainingFrame = windowFrames.mParentFrame;
            layoutXDiff = 0;
            layoutYDiff = 0;
        } else {
            windowFrames.mContainingFrame.set(getBounds());
            ......

            layoutDisplayFrame = mTmpRect2;
            layoutDisplayFrame.set(windowFrames.mDisplayFrame);
            windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
            layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
            layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
            layoutContainingFrame = mInsetFrame;
            mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
            subtractInsets(windowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
                    mTmpRect);
            if (!layoutInParentFrame()) {
                subtractInsets(windowFrames.mContainingFrame, layoutContainingFrame,
                        windowFrames.mParentFrame, mTmpRect);
                subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
                        mTmpRect);
            }
            layoutDisplayFrame.intersect(layoutContainingFrame);
        }

        final int pw = windowFrames.mContainingFrame.width();
        final int ph = windowFrames.mContainingFrame.height();

        final int fw = windowFrames.mFrame.width();
        final int fh = windowFrames.mFrame.height();
        // step4.1 applyGravityAndUpdateFrame()
        applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
        // 设置mFrame偏移
        // Offset the actual frame by the amount layout frame is off.
        windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
        // 默认mCompatFrame等于mFrame
        windowFrames.mCompatFrame.set(windowFrames.mFrame);
        // 只有兼容模式或者窗口缩放情况下mCompatFrame需要乘以缩放比例
        if (hasCompatScale()) {
            // Also the scaled frame that we report to the app needs to be
            // adjusted to be in its coordinate space.
            windowFrames.mCompatFrame.scale(mInvGlobalScale);
        }
        ......

        // 设置mRealFrame,默认等于mFrame
        // Calculate relative frame
        windowFrames.mRelFrame.set(windowFrames.mFrame);
        WindowContainer parent = getParent();
        int parentLeft = 0;
        int parentTop = 0;
        if (mIsChildWindow) {
            parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
            parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
        } else if (parent != null) {
            final Rect parentBounds = parent.getBounds();
            parentLeft = parentBounds.left;
            parentTop = parentBounds.top;
        }
        windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
                windowFrames.mFrame.top - parentTop);

        
    }

step4.1 applyGravityAndUpdateFrame()
    private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
            Rect displayFrame) {
        final int pw = containingFrame.width();
        final int ph = containingFrame.height();
        final Task task = getTask();
        final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
        final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;

        // We need to fit it to the display if either
        // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
        // for the taskless windows)
        // b) If it's a secondary app window, we also need to fit it to the display unless
        // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
        // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
        // the display.
        final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
                || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
        float x, y;
        int w,h;

        final boolean hasCompatScale = hasCompatScale();
        // 计算宽高和坐标
        if ((mAttrs.flags & FLAG_SCALED) != 0) {
            if (mAttrs.width < 0) {
                w = pw;
            } else if (hasCompatScale) {
                w = (int)(mAttrs.width * mGlobalScale + .5f);
            } else {
                w = mAttrs.width;
            }
            if (mAttrs.height < 0) {
                h = ph;
            } else if (hasCompatScale) {
                h = (int)(mAttrs.height * mGlobalScale + .5f);
            } else {
                h = mAttrs.height;
            }
        } else {
            if (mAttrs.width == MATCH_PARENT) {
                w = pw;
            } else if (hasCompatScale) {
                w = (int)(mRequestedWidth * mGlobalScale + .5f);
            } else {
                w = mRequestedWidth;
            }
            if (mAttrs.height == MATCH_PARENT) {
                h = ph;
            } else if (hasCompatScale) {
                h = (int)(mRequestedHeight * mGlobalScale + .5f);
            } else {
                h = mRequestedHeight;
            }
        }

        if (hasCompatScale) {
            x = mAttrs.x * mGlobalScale;
            y = mAttrs.y * mGlobalScale;
        } else {
            x = mAttrs.x;
            y = mAttrs.y;
        }

       ......
    }
    
boolean hasCompatScale() {
        return (mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord)) && !mIsChildWindow;
    }

    /**
     * @return {@code true} if the application runs in size compatibility mode.
     * @see android.content.res.CompatibilityInfo#supportsScreen
     * @see ActivityRecord#hasSizeCompatBounds()
     */
static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
        return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
                || (windowToken != null && windowToken.hasSizeCompatBounds()
                // Exclude starting window because it is not displayed by the application.
                && attrs.type != TYPE_APPLICATION_STARTING);
    }

本方法主要是处理兼容模式或者自定义窗口缩放情况下width和height和坐标的校正。hasCompatScale就是表示兼容模式或者自定义窗口缩放。startingWindow在androidT上进行了兼容,S上显示有问题。