美文网首页
Android13 WMS窗口相关流程(四)

Android13 WMS窗口相关流程(四)

作者: Bfmall | 来源:发表于2023-10-25 15:07 被阅读0次

    接着第三篇:https://www.jianshu.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

    相关文章

      网友评论

          本文标题:Android13 WMS窗口相关流程(四)

          本文链接:https://www.haomeiwen.com/subject/qrjjidtx.html