Android13 WMS窗口相關流程(四)

接著第三篇:http://www.lxweimin.com/p/a10944f9d5e3?v=1698141883941

2.2 窗口位置計算

當WindowState加入到WindowToken并調整z-order之后,客戶端會再次調用WMS執行窗口布局。
主要做了這三件事:
1.接收客戶端請求
2.創建SurfaceControl
3.窗口大小位置計算

2.2.1 接收客戶端請求

與addWindow流程的調用過程類似,WindowManagerService.relayoutWindow也是由客戶端通過Session來調用的。
首先我們來看一下客戶端給我們傳遞了哪些參數吧。
window:是WMS與客戶端通信的Binder。
attrs:窗口的布局屬性,根據attrs提供的屬性來布局窗口。
requestWidth、requestHeight:客戶端請求的窗口尺寸。
viewFlags:窗口的可見性。包括VISIBLE(0,view可見),INVISIBLE(4,view不可見,但是仍然占用布局空間)GONE(8,view不可見,不占用布局空間)
flags:定義一些布局行為。
outFrames:返回給客戶端的,保存了重新布局之后的位置與大小。
mergedConfiguration:相關配置信息。
outSurfaceControl:返回給客戶端的surfaceControl。
outInsetsState:用來保存系統中所有Insets的狀態。
outActiveControls:InSetsSourceControl數組。
outSyncSeqIdBundle:與布局同步有關。
Session調用WMS.relayoutWindow將客戶端傳入的參數傳遞給WMS。
代碼路徑:framework/services/core/java/com/android/server/wm/Session.java

    @Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
        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,
                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSyncSeqIdBundle);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                + Binder.getCallingPid());
        return res;
    }

2.2.2 relayoutWindow

在WMS.relayoutWindow中主要做了以下事情:
1.根據客戶端傳過來的IWindow在mWindowMap獲取窗口添加階段創建的WindowState。
2.設置DisplayContent.mLayoutNeeded以及shouldRelayout標志位
3.Surface的創建流程。
4.窗口尺寸的計算以及Surface的狀態變更。
代碼路徑:framework/services/core/java/com/android/server/wm/WindowManagerService.java

    public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
        ......
        synchronized (mGlobalLock) {
            /*1.根據客戶端傳過來的Iwindow從mWindowMap中獲取對應的WindowState*/
            final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            //獲取DisplayContent、DisplayPolicy以及WindowStateAnimator 
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (viewVisibility != View.GONE) {
                //根據客戶端請求的窗口大小設置WindowState的requestedWidth, requestedHeight
                //并設置WindowState.mLayoutNeeded為true
                win.setRequestedSize(requestedWidth, requestedHeight);
            }
            ......
            //根據請求的寬帶和高度窗口縮放比例
            win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
            ......
            //獲取原來window的可見性,此時為INVISIBLE
            final int oldVisibility = win.mViewVisibility;
            ......
            //代表現在沒有surface但應該很快就有標志位
            win.mRelayoutCalled = true;
            win.mInRelayout = true;
            //將當前窗口的可見性有原來的INVISIBLE調整為VISIBLE
            win.setViewVisibility(viewVisibility);
            ProtoLog.i(WM_DEBUG_SCREEN_ON,
                    "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                            viewVisibility, new RuntimeException().fillInStackTrace());
            /*2.1.將displayContent中的布局標志為mLayoutNeeded置為true*/
            win.setDisplayLayoutNeeded();
            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

            // We should only relayout if the view is visible, it is a starting window, or the
            // associated appToken is not hidden.
            /*2.2.判斷是否允許relayout,此時為true*/
            //判斷條件:view可見且(activityRecord不為空,或者布局類型為TYPE_APPLICATION_STARTING,或者窗口已經告訴客戶端可以顯示)
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
            ......
            // Create surfaceControl before surface placement otherwise layout will be skipped
            // (because WS.isGoneForLayout() is true when there is no surface.
            /*3.surface的創建流程*/
            if (shouldRelayout) {
                try {
                    //進入creatSurfaceControl開始創建SurfaceControl
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    ......
                    return 0;
                }
            }

            // 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.
            /*4.窗口尺寸的計算以及Surface的狀態變更*/
            //WindowSurfacePlacer在WMS初始化的時候創建
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
            ......
            //填充計算好的frame返回給客戶端,更新mergedConfiguration對象
            win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
                    false /* useLatestConfig */, shouldRelayout);

            // Set resize-handled here because the values are sent back to the client.
            win.onResizeHandled();
            ......
        }

        Binder.restoreCallingIdentity(origId);
        //返回result
        return result;
    }

2.2.3 創建SurfaceControl

在relayoutWindow中創建SurfaceControl
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
關于SurfaceControl的創建在WMS中主要做兩件事:
1.調用WindwoStateAnimator執行具體的SurfaceControl的創建。
2.將創建的SurfaceControl賦值給客戶端的outSurfaceControl。
代碼路徑:framework/services/core/java/com/android/server/wm/WindowManagerService.java

    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
        ......
        WindowSurfaceController surfaceController;
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
            /*
             * WindowStateAnimator用來幫助WindowState管理animator和surface基本操作的
             * 1.WMS將創建的surfaceContorl的操作交給windowAnimator來處理
             */
            surfaceController = winAnimator.createSurfaceLocked();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        if (surfaceController != null) {
            /*2.將WMS的SurfaceControl賦值給客戶端的outSurfaceControl*/
            surfaceController.getSurfaceControl(outSurfaceControl);
            ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);

        } else {
            // For some reason there isn't a surface.  Clear the
            // caller's object so they see the same state.
            ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
            outSurfaceControl.release();
        }

        return result;
    }

在WindowStateAnimator中創建SurfaceControl主要經過以下三個步驟:
1.重置Surface標志位,變更mDrawState狀態為DRAW_PENDING。
2.通過實例化WindowSurfaceController來創建SurfaceControl。
3.處理Surface標志位,將其置為true,標志著當前WindowState已經有surface了
代碼路徑:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java

   WindowSurfaceController createSurfaceLocked() {
        final WindowState w = mWin;
        
        //首先判斷是否存在mSurfaceController
        if (mSurfaceController != null) {
            return mSurfaceController;
        }
        /*1.1.設置WindowState的mHasSurface設置為false*/
        w.setHasSurface(false);

        ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
        /*1.2.將WindowStateAnimator中的DrawState設置為DRAW_PENDING*/
        resetDrawState();

        mService.makeWindowFreezingScreenIfNeededLocked(w);

        /*1.3.將surface創建flag設置為hidden*/
        int flags = SurfaceControl.HIDDEN;
        //獲取windowState的布局參數
        final WindowManager.LayoutParams attrs = w.mAttrs;

        // Set up surface control with initial size.
        try {
            ......
            /*2.創建WindowSurfaceController*/
            //attrs.getTitle().toString()為當前activity的全路徑名
            //format為位圖格式
            //flags為surface創建的標志位(如:HIDDED(0x04,surface創建為隱藏),SKIP_SCREENSHOT(0x040,截屏時跳過此圖層將不會包含在非主顯示器上),SECURE(0X080,禁止復制表面的內容,屏幕截圖和次要的非安全顯示將呈現黑色內容而不是surface內容)等)
            //attrs.type為窗口類型
            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
                    flags, this, attrs.type);
            mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
                    & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);

            /*3.將WindowState的hasSurface標志設置為true,標志著道歉WindowState已經有surface了*/
            w.setHasSurface(true);
            ......
        } catch (OutOfResourcesException e) {
            ......
        } catch (Exception e) {
            ......
        }
        ......
        return mSurfaceController;
    }

SurfaceControl的創建過程為典型的建造者模式
接下來看看WindowSurfaceController的構造方法
代碼路徑:framework/services/core/java/com/android/server/wm/WindowSurfaceController.java

WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
            int windowType) {
        //設置WindowStateAnimator
        mAnimator = animator;
        //窗口名
        title = name;
        //WMS對象
        mService = animator.mService;
        //WindowState對象
        final WindowState win = animator.mWin;
        //窗口類型
        mWindowType = windowType;
        //IWindowSession對象
        mWindowSession = win.mSession;

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
        //makeSurface最終會調用到DisplayContent的makeChildSurface方法,返回SurfaceControl.Builder
        final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
                .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
                .setCallsite("WindowSurfaceController");
        ......
        //獲取SurfaceControl實例對象
        mSurfaceControl = b.build();

        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

兩個關鍵方法win.makeSurface()和b.build()
1.final SurfaceControl.Builder b = win.makeSurface()
我們先來看看win.makeSurface(),windowState中沒有makeSurface()方法,因此調用其父類WindowContainer的makeSurface()方法
代碼路徑:framework/services/core/java/com/android/server/wm/WindowContainer.java

    SurfaceControl.Builder makeSurface() {
        final WindowContainer p = getParent();
        return p.makeChildSurface(this);
    }
        /**
     * @param child The WindowContainer this child surface is for, or null if the Surface
     *              is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
     */
    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
        final WindowContainer p = getParent();
        // Give the parent a chance to set properties. In hierarchy v1 we rely
        // on this to set full-screen dimensions on all our Surface-less Layers.
        return p.makeChildSurface(child)
                .setParent(mSurfaceControl);
    }

最終會調用到DisplayContent的makeChildSurface
代碼路徑:framework/services/core/java/com/android/server/wm/DisplayContent.java

    @Override
    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
        //此時child為WindowState
        //獲取SurfaceSession,SurfaceSession的創建在Session.windowAddedLocked中,其最開始調用在WindowManagerService.addWindow中win.attach()中創建
        SurfaceSession s = child != null ? child.getSession() : getSession();
        //返回SurfaceControl.Builder
        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
        if (child == null) {
            return b;
        }
        //設置SurfaceControl.Builder的name以及parent
        return b.setName(child.getName())
                .setParent(mSurfaceControl);
    }

最終會調用到DisplayContent的makeChildSurface
代碼路徑:framework/services/core/java/com/android/server/wm/DisplayContent.java

    @Override
    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
        //此時child為WindowState
        //獲取SurfaceSession,SurfaceSession的創建在Session.windowAddedLocked中,其最開始調用在WindowManagerService.addWindow中win.attach()中創建
        SurfaceSession s = child != null ? child.getSession() : getSession();
        //返回SurfaceControl.Builder
        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
        if (child == null) {
            return b;
        }
        //設置SurfaceControl.Builder的name以及parent
        return b.setName(child.getName())
                .setParent(mSurfaceControl);
    }

2.mSurfaceControl = b.build();
再來看看b.build(),調用SurfaceControl中的build
代碼路徑:framework/core/java/android/view/SurfaceControl.java

    /**
      * Construct a new {@link SurfaceControl} with the set parameters. The builder
      * remains valid.
      */
     @NonNull
     public SurfaceControl build() {
        //檢查width以及height,初始都應該為0
         if (mWidth < 0 || mHeight < 0) {
             throw new IllegalStateException(
                     "width and height must be positive or unset");
         }
         if ((mWidth > 0 || mHeight > 0) && (isEffectLayer() || isContainerLayer())) {
             throw new IllegalStateException(
                     "Only buffer layers can set a valid buffer size.");
         }
    
         if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
             setBLASTLayer();
         }
    //創建SurfaceControl的實例
         return new SurfaceControl(
                 mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
                 mLocalOwnerView, mCallsite);
     }
    
   /**
    * @param session  The surface session, must not be null.
    * @param name     The surface name, must not be null.
    * @param w        The surface initial width.
    * @param h        The surface initial height.
    * @param flags    The surface creation flags.
    * @param metadata Initial metadata.
    * @param callsite String uniquely identifying callsite that created this object. Used for
    *                 leakage tracking.
    * @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
    */
    private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
         SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
         String callsite)
                 throws OutOfResourcesException, IllegalArgumentException {
     if (name == null) {
         throw new IllegalArgumentException("name must not be null");
     }
    
     mName = name;
     mWidth = w;
     mHeight = h;
     mLocalOwnerView = localOwnerView;
     //創建Parcel用來傳遞數據
     Parcel metaParcel = Parcel.obtain();
     try {
        ......
        //調用native層
         mNativeObject = nativeCreate(session, name, w, h, format, flags,
                 parent != null ? parent.mNativeObject : 0, metaParcel);
     } finally {
         metaParcel.recycle();
     }
     if (mNativeObject == 0) {
         throw new OutOfResourcesException(
                 "Couldn't allocate SurfaceControl native object");
     }
     mNativeHandle = nativeGetHandle(mNativeObject);
     mCloseGuard.openWithCallSite("release", callsite);
    }


SurfaceControl的構造方法調用完成后,返回查看前面
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);

2.2.4 計算窗口大小位置

在relayoutWindow中計算窗口大小位置
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
該流程我們分為三部分介紹:
1.該部分處理有關窗口布局循環的邏輯。
2.該部分處理Surface的狀態變更,以及調用layoutWindowLw的流程。
3.計算窗口位置大小。

1.處理窗口布局循環
performSurfacePlacement是一個確定所有窗口的Surface的如何擺放,如何顯示、顯示在什么位置、顯示區域多大的一個入口方法。
該方法主要設置了布局的循環條件,當mTraversalScheduled 標志位為true,且loopCount大于0。將會調用performSurfacePlacementLoop執行布局操作。
代碼路徑:framework/services/core/java/com/android/server/wm/WindowSurfacePlacer.java

    final void performSurfacePlacement(boolean force) {
        if (mDeferDepth > 0 && !force) {
            mDeferredRequests++;
            return;
        }
        //將循環的最大次數設置為6次
        int loopCount = 6;
        do {
            //將該標志為設置為false
            mTraversalScheduled = false;
            //執行窗口布局操作
            performSurfacePlacementLoop();
            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
            loopCount--;
        //只有當mTraversalScheduled為true且循環次數大于0時,才會再次循環執行布局
        } while (mTraversalScheduled && loopCount > 0);
        mService.mRoot.mWallpaperActionPending = false;
    }

performSurfacePlacementLoop方法主要做兩件事:
1.調用RootWindowContainer對所有窗口執行布局操作,
2.處理是否再次進行布局的邏輯。如果DisplayContent.mLayoutNeeded標志位為true且布局循環次數小于6次,則會將mTraversalScheduled標志位置為true,在performSurfacePlacement中會再次調用performSurfacePlacementLoop。

    private void performSurfacePlacementLoop() {
        //若當前已經進行布局操作,則無需重復調用直接返回
        if (mInLayout) {
            ......
            return;
        }
        ......
        //將該標志位置為true,表示正在處于布局過程中
        mInLayout = true;
        ......
        try {
            /*1.調用RootWindowContainer的performSurfacePlacement()方法對所有窗口執行布局操作*/
            mService.mRoot.performSurfacePlacement();

            mInLayout = false;

            if (mService.mRoot.isLayoutNeeded()) {
                /*2.若需要布局,且布局次數小于6次,則需要再次請求布局*/
                if (++mLayoutRepeatCount < 6) {
                    //該方法中會將mTraversalScheduled標志位設置位true
                    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);
        }
    }

2.處理所有Surface的狀態變更,以及調用layoutWindowLw的流程
mService.mRoot.performSurfacePlacement();
上面說到在RootWindowContainer.performSurfacePlacement()中調用了performSurfaceNoTrace()方法,該方法為實際的處理布局的方法,主要處理以下流程:
1.如果有焦點變化,更新焦點。
2.執行窗口尺寸計算,surface狀態變更等操作。
3.將Surface狀態變更為HAS_DRAWN,觸發App觸發動畫。該過程在finishdrawing()中再詳細分析。
4.如果壁紙有變化,更新壁紙。
5.再次處理焦點變化。
6.如果過程中由size或者位置變化,則通知客戶端重新relayout。
7.銷毀不可見的窗口
代碼路徑:framework/services/core/java/com/android/server/wm/RootWindowContainer.java

    void performSurfacePlacement() {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
        try {
            //調用performSurfacePlacementNoTrace()
            performSurfacePlacementNoTrace();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    // "Something has changed!  Let's make it correct now."
    // TODO: Super long method that should be broken down...
    void performSurfacePlacementNoTrace() {
        ......
        /*1.如果有焦點變化,更新焦點*/
        if (mWmService.mFocusMayChange) {
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(
                    UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
        }
        ......
        
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
        //開啟事務,獲取GlobalTransactionWrapper對象
        mWmService.openSurfaceTransaction();
        try {
            /*2.執行窗口尺寸計算,surface狀態變更等操作*/
            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");
            }
        }
        ......
        /*3.將Surface狀態變更為HAS_DRAWN,觸發App觸發動畫。該過程在“2.3.3mDrawState變更為HAS_DRAW”流程中再詳細分析*/
        checkAppTransitionReady(surfacePlacer);
        ......
        /*4.遍歷所有DisplayContent,如果壁紙有變化,更新壁紙*/
        for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
            final DisplayContent displayContent = mChildren.get(displayNdx);
            //判斷DisplayContent的壁紙是否需要改變
            if (displayContent.mWallpaperMayChange) {
                ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change!  Adjusting");
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                if (DEBUG_LAYOUT_REPEATS) {
                    surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
                            displayContent.pendingLayoutChanges);
                }
            }
        }
        /*5.在此處理焦點變化*/
        if (mWmService.mFocusMayChange) {
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                    false /*updateInputWindows*/);
        }
        ......
        /*6.如果過程中size或者位置變化,則通知客戶端重新relayout*/
        handleResizingWindows();

        if (mWmService.mDisplayFrozen) {
            ProtoLog.v(WM_DEBUG_ORIENTATION,
                    "With display frozen, orientationChangeComplete=%b",
                    mOrientationChangeComplete);
        }
        if (mOrientationChangeComplete) {
            if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
            }
            mWmService.stopFreezingDisplayLocked();
        }

        // Destroy the surface of any windows that are no longer visible.
        /*7.銷毀不可見的窗口*/
        i = mWmService.mDestroySurface.size();
        if (i > 0) {
            do {
                i--;
                WindowState win = mWmService.mDestroySurface.get(i);
                win.mDestroying = false;
                final DisplayContent displayContent = win.getDisplayContent();
                if (displayContent.mInputMethodWindow == win) {
                    displayContent.setInputMethodWindowLocked(null);
                }
                if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
                win.destroySurfaceUnchecked();
            } while (i > 0);
            mWmService.mDestroySurface.clear();
        }
        ......
    }


在applySurfaceChangesTransaction();方法中其主要執行:
1.水印、StrictMode警告框以及模擬器顯示的布局。
2.遍歷所有DisplayContent執行其applySurfaceChangesTransaction
我們一起看看這個方法

    private void applySurfaceChangesTransaction() {
        mHoldScreenWindow = null;
        mObscuringWindow = null;

        // TODO(multi-display): Support these features on secondary screens.
        /*1.水印、StrictMode警告框以及模擬器顯示的布局*/
        //獲取手機默認DisplayContent的信息
        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);
        }
        //布局StrictMode警告框
        if (mWmService.mStrictModeFlash != null) {
            mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
        }
        //布局模擬器顯示覆蓋
        if (mWmService.mEmulatorDisplayOverlay != null) {
            mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
                    mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
        }
        /*2.遍歷RootWindowContainer下所有DisplayContent執行其applySurfaceChangesTransaction()*/
        final int count = mChildren.size();
        for (int j = 0; j < count; ++j) {
            final DisplayContent dc = mChildren.get(j);
            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);
    }

接下來繼續跟蹤dc.applySurfaceChangesTransaction();
該方法主要
1.遍歷所有窗口,計算窗口的布局大小,具體流程查看performLayoutNoTrace。(主要跟蹤點)
2.surface的狀態更改。(見“2.3.3mDrawState變更為HAS_DRAW”流程”)
3.處理surface的位置、大小以及顯示等。(見“2.3.4 show Surface”流程”)
代碼路徑:framework/services/core/java/com/android/server/wm/DisplayContent.java

    void applySurfaceChangesTransaction() {
        //獲取WindowSurfacePlacer 
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
        ......
        // Perform a layout, if needed.
        /*1.執行布局,該方法最終會調用performLayoutNoTrace,計算窗口的布局參數*/
        performLayout(true /* initial */, false /* updateInputWindows */);
        ......
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
        try {
            /*2.遍歷所有窗口,主要是改變surface的狀態。見“2.3.3mDrawState變更為HAS_DRAW”流程*/
            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        /*3.處理各個surface的位置、大小以及是否要在屏幕上顯示等。后面finishDrawing()流程中再跟蹤*/
        prepareSurfaces();
        ......
    }

繼續跟蹤performLayout(true /* initial /, false / updateInputWindows */);
該方法主要就是調用performLayoutNoTrace()方法,首先判斷布局標志位mLayoutNeeded,該標志位在WMS.relayoutWindow中被置為true。
false則直接返回不會進行布局操作。
true則分別遍歷父窗口和子窗口進行布局,我們暫且只分析父窗口的布局操作,查看mPerformLayout。

    void performLayout(boolean initial, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
        try {
            //調用performLayoutNoTrace
            performLayoutNoTrace(initial, updateInputWindows);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
         /*1.判斷是否需要布局,不需要則直接返回,即判斷布局標志位mLayoutNeeded是否為true*/
        if (!isLayoutNeeded()) {
            return;
        }
        //將DisplayContent.mLayoutNeeded屬性置為false
        clearLayoutNeeded();
        ......
        // First perform layout of any root windows (not attached to another window).
        /*2.對所有頂級窗口進行布局*/
        //最終會回調mPerformLayout
        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.
        /*3.處理子窗口的布局*/
        //最終會回調mPerformLayoutAttached
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
        ......
    }

forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
當遍歷到DisplayContent下的每個窗口時都會執行mPerformLayout,該方法會將WindowState.mLayoutNeeded標志位置false,并將具體的布局操作交給DisplayPolicy進行處理,見“3. 計算窗口位置大小“。

    private final Consumer<WindowState> mPerformLayout = w -> {
        //如果當前窗口為子窗口則直接返回
        if (w.mLayoutAttached) {
            return;
        }

        // 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) {
            if (mTmpInitial) {
                //設置窗口布局WindowFrames.mContentChanged為false
                w.resetContentChanged();
            }
            w.mSurfacePlacementNeeded = true;
            //此處將WindowState.mLayoutNeeded標志位置為false
            w.mLayoutNeeded = false;
            //判斷當前窗口是否是第一次布局
            final boolean firstLayout = !w.isLaidOut();
            //調用DisplayPolicy.layoutWindowLw進行布局,根據DisplayFrames對象對WindowState.mWindowFrames中的各個Rect對象屬性進行確定
            getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
            w.mLayoutSeq = mLayoutSeq;

            // If this is the first layout, we need to initialize the last frames and inset values,
            // as otherwise we'd immediately cause an unnecessary resize.
            if (firstLayout) {
                // The client may compute its actual requested size according to the first layout,
                // so we still request the window to resize if the current frame is empty.
                if (!w.getFrame().isEmpty()) {
                    w.updateLastFrames();
                }
                w.onResizeHandled();
            }

            if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrame()
                    + " mParentFrame=" + w.getParentFrame()
                    + " mDisplayFrame=" + w.getDisplayFrame());
        }
    };

3.計算窗口位置大小
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
layoutWindowLw主要做了以下三件事
1.首先會獲DisplayFrames:DisplayContent新建時創建,內部數據由屏幕提供。
2.其次調用WindowLayout.computeFrames計算窗口布局大小。
3.最后調用WindowState.setFrames將計算的布局參數賦值給當前窗口的windowFrames。
代碼路徑:framework/services/core/java/com/android/server/wm/DisplayPolicy.java

    /**
     * Called for each window attached to the window manager as layout is proceeding. The
     * implementation of this function must take care of setting the window's frame, either here or
     * in finishLayout().
     *
     * @param win The window being positioned.
     * @param attached For sub-windows, the window it is attached to; this
     *                 window will already have had layoutWindow() called on it
     *                 so you can use its Rect.  Otherwise null.
     * @param displayFrames The display frames.
     */
    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
        //判斷是否需要跳過布局
        if (win.skipLayout()) {
            return;
        }

        // This window might be in the simulated environment.
        // We invoke this to get the proper DisplayFrames.
        /*1.獲取DisplayFrames*/
        displayFrames = win.getDisplayFrames(displayFrames);
        //獲取某個方向的窗口布局參數
        final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
        //null
        final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;

        // If this window has different LayoutParams for rotations, we cannot trust its requested
        // size. Because it might have not sent its requested size for the new rotation.
        final boolean trustedSize = attrs == win.mAttrs;
        final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
        final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
        /*2.調用WindowLayout.computeFrames計算窗口布局大小*/
        mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
                win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
                win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
                sTmpClientFrames);
        /*3.將計算的布局參數賦值給windowFrames*/
        win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
    }

先來看看computeFrames

mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
                win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
                win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
                sTmpClientFrames);

留坑

再來看看setFrames

 win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);

留坑

————————————————
版權聲明:本文為CSDN博主「yi諾千金」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/yimelancholy/article/details/130339779

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容