Android10 对Activity大小的测量

效果图

 

本文章是针对Android10版本梳理的内容,如果你的Android源码是23之前,可以参考其他博主对Activity的测量,因为在早期版本很多业务都是写在WindowManagerService类中,在新版本,google官方做了很多重构,本文除了会设计到Activity的测量外,还会对StatusBar,导航栏等的测量有简单的描述,那我们开始把

 上面是Activity窗口的Content区域示意图

Content Region是Activity剔除掉状态栏所占用的区域,Content Insets又是什么呢?Activity窗口的四周都有一块类似状态栏的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为内容区域,而被剔除出来的区域所组成的区域就称为内容边衬区域(Content Insets)

 

Activity窗口的Visible区域示意图

我们为什么会看这个图呢,因为除了图一以外,还有输入法存在的情况,那么此时Activity的大小就会受到输入法的弹窗模式Soft Input Mode影响,如果输入法弹出的窗口不会导致Activity变化,输入法的窗口在Activity上面,导致Activity被部分遮挡,那么剩余可见的部分被称为可见区域(Visible Region),如果Activity四周也存在,内容边衬区域(Content Insets),把这些区域剔除以后剩下的区域们也被称为可见区域(Visible Region),在大多数情况下,Activity窗口的内容区域和可见区域的大小是一致的,而状态栏和输入法窗口所占用的区域又称为屏幕装饰区

所以根据上面的信息可知,Activity的大小就是,根据边衬区域(Content Insets),状态栏,和输入法窗口来计算出的。

首先我们来看一下23之前的版本是怎么计算如下图所示

 上图逻辑不做过多的解释,因为在新的版本google官方对WMS这一块做了重构,如需要了解早期版本对Activity窗口的处理,可以自行百度,资料很多

下面我们正式进入Activity大小的计算过程

在早期版本主要开始点是在ViewRoot.performTraversals 这个方法,ViewRoot是Android2.2以前的,2.2之后就被ViewRootImpl替代了,所以我们的开始分析方法从ViewRootImpl的performTraversals开始.

performTraversals()是ViewRootImpl的一个方法,performTraversals()作为三大流程的起点,创建、参数改变、界面刷新等时都有可能会需要从根部开始measure、layout、draw,就会调用到它。

   // cache mView since it is used so much below...
        // 这里的mView在setView中被赋值为DecorView
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }

        if (host == null || !mAdded)
            return;

        mIsInTraversal = true;
        mWillDrawSoon = true;
        boolean windowSizeMayChange = false;
        boolean surfaceChanged = false;
        WindowManager.LayoutParams lp = mWindowAttributes;

        int desiredWindowWidth;
        int desiredWindowHeight;

        final int viewVisibility = getHostVisibility();
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                // Also check for possible double visibility update, which will make current
                // viewVisibility value equal to mViewVisibility and we may miss it.
                || mAppVisibilityChanged);
        mAppVisibilityChanged = false;
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));

        WindowManager.LayoutParams params = null;
        if (mWindowAttributesChanged) {
            mWindowAttributesChanged = false;
            surfaceChanged = true;
            params = lp;
        }
        CompatibilityInfo compatibilityInfo =
                mDisplay.getDisplayAdjustments().getCompatibilityInfo();
        if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
            params = lp;
            mFullRedrawNeeded = true;
            mLayoutRequested = true;
            if (mLastInCompatMode) {
                params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                mLastInCompatMode = false;
            } else {
                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                mLastInCompatMode = true;
            }
        }

        mWindowAttributesChangesFlag = 0;
        // 用来保存窗口宽度和高度,来自于全局变量mWinFrame,这个mWinFrame保存了窗口最新尺寸
        Rect frame = mWinFrame;
        // mFirst在构造器中被初始化为true,表示第一次performTraversals()
        if (mFirst) {
            // 设置需要全部重新draw
            mFullRedrawNeeded = true;
            // 需要重新layout
            mLayoutRequested = true;

            final Configuration config = mContext.getResources().getConfiguration();
            // 根据lp.type判断要绘制的窗口是否包含状态栏,然后确定要绘制的Decorview的高度和宽度
            // 有的话就去掉
            if (shouldUseDisplaySize(lp)) {
                // NOTE -- system code, won't try to do compat mode.
                // 宽度和高度为整个屏幕的值
                Point size = new Point();
                mDisplay.getRealSize(size);
                desiredWindowWidth = size.x;
                desiredWindowHeight = size.y;
            } else {
               //它的当前宽度desiredWindowWidth和当前高度desiredWindowHeight就等于保存在ViewRoot类的成员变量mWinFrame中的宽度和高度值。
                desiredWindowWidth = mWinFrame.width();
                desiredWindowHeight = mWinFrame.height();
            }

            // We used to use the following condition to choose 32 bits drawing caches:
            // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
            // However, windows are now always 32 bits by default, so choose 32 bits
            mAttachInfo.mUse32BitDrawingCache = true;
            mAttachInfo.mWindowVisibility = viewVisibility;
            mAttachInfo.mRecomputeGlobalAttributes = false;
            mLastConfigurationFromResources.setTo(config);
            mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
            // Set the layout direction if it has not been set before (inherit is the default)
            if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
                host.setLayoutDirection(config.getLayoutDirection());
            }
            //dispatchAttachedToWindow被调用
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
        } else {
            // 如果不是第一次performTraversals()  
            // 它的当前宽度和高度就从之前的保存在ViewRootImpl类的成员变量mWinFrame/frame获取
            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
            //Activity窗口主动上一次请求WindowManagerService服务计算得到的宽度mWidth和高度mHeight不等于Activity窗
            //口的当前宽度desiredWindowWidth和当前高度desiredWindowHeight,那么就说明Activity窗口的大小发生了变化
            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                // 需要进行全部重绘以适应新的窗口尺寸
                mFullRedrawNeeded = true;
                // 需要重新布局
                mLayoutRequested = true;
                // window窗口大小改变
                windowSizeMayChange = true;
            }
        }

其中关键部分我加入了中文注释,当然,部分也是参考其他大佬的经验分享,上面主要做了什么事情呢,如果是第一次调用,mFirst则是true,设置desiredWindowWidth,和desiredWindowHeight的大小,如果不是第一次调用,则会去判断上一次WindowManagerService测量的大小,是否和当前的窗口大小不一样则重新设置不同的属性,mWidth和mHeight也是用来描述Activity窗口当前的宽度和高度的,但是它们的值是由应用程序进程上一次主动请求WindowManagerService服务计算得到的,不会发生变化,直到WindowManagerService服务主动请求应用程序进程修改的

1.接着看下面的代码

        if (viewVisibilityChanged) {
            mAttachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
            if (viewUserVisibilityChanged) {
                host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
            }
            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                endDragResizing();
                destroyHardwareResources();
            }
            if (viewVisibility == View.GONE) {
                // After making a window gone, we will count it as being
                // shown for the first time the next time it gets focus.
                mHasHadWindowFocus = false;
            }
        }

        // Non-visible windows can't hold accessibility focus.
        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
            host.clearAccessibilityFocus();
        }

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(mAttachInfo.mHandler);

这段代码没有什么可以讲的,根据viewVisibilityChanged的状态,去调用host的周期方法回调

2.是否重置窗口尺寸,窗口尺寸有变化

//调整窗口大小
        // 同时满足三个条件
        // 1.layoutRequested为true,已经发起了一次新的layout。
        // 2.上面赋值的窗口尺寸可能发生改变
        // 3.上面measureHierarchy()中测量的值和上一次保存的值不同 或
        // 宽或高设置为wrap_content,WindowManagerService服务请求Activity窗口设置的宽度frame.width()和高度frame.height()与窗口期望大小不一致,而且与Activity窗口上一次请求WindowManagerService服务计算的宽度mWidth和高度mHeight也不一致
        boolean windowShouldResize = layoutRequested && windowSizeMayChange
            && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
                || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.width() < desiredWindowWidth && frame.width() != mWidth)
                || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.height() < desiredWindowHeight && frame.height() != mHeight));
        windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;

        // If the activity was just relaunched, it might have unfrozen the task bounds (while
        // relaunching), so we need to force a call into window manager to pick up the latest
        // bounds.
        // 如果activity重新启动,需要强制通过wms获取最新的值
        windowShouldResize |= mActivityRelaunched;

3.调用WMS计算保存,并重新赋值

// 第一次performTraversals() 或 窗口尺寸有变化 或 insets有变化 或 窗口可见性有变化
        // 或 params窗口属性有变化指向了mWindowAttributes 或 强迫窗口下一次重新layout
        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            mForceNextWindowRelayout = false;

            if (isViewVisible) {
                // If this window is giving internal insets to the window
                // manager, and it is being added or changing its visibility,
                // then we want to first give the window manager "fake"
                // insets to cause it to effectively ignore the content of
                // the window during layout.  This avoids it briefly causing
                // other windows to resize/move based on the raw frame of the
                // window, waiting until we can finish laying out this window
                // and get back to the window manager with the ultimately
                // computed insets.
                // 如果computesInternalInsets发生改变  并且  是第一次performTraversals()或窗口从不可见变为可见
                // 就置insetsPending为true
                insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
            }

            if (mSurfaceHolder != null) {
                mSurfaceHolder.mSurfaceLock.lock();
                mDrawingAllowed = true;
            }

            boolean hwInitialized = false;
            boolean contentInsetsChanged = false;
            boolean hadSurface = mSurface.isValid();

            try {
                if (DEBUG_LAYOUT) {
                    Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
                            host.getMeasuredHeight() + ", params=" + params);
                }

                if (mAttachInfo.mThreadedRenderer != null) {
                    // relayoutWindow may decide to destroy mSurface. As that decision
                    // happens in WindowManager service, we need to be defensive here
                    // and stop using the surface in case it gets destroyed.
                    if (mAttachInfo.mThreadedRenderer.pause()) {
                        // Animations were running so we need to push a frame
                        // to resume them
                        mDirty.set(0, 0, mWidth, mHeight);
                    }
                    mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
                }

                // 调用relayoutWindow()(使用IPC)去请求WMS 重新计算窗口尺寸以及insets大小
                // 计算完毕之后,Activity窗口的大小就会保存在成员变量mWinFrame中
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

                if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
                        + " overscan=" + mPendingOverscanInsets.toShortString()
                        + " content=" + mPendingContentInsets.toShortString()
                        + " visible=" + mPendingVisibleInsets.toShortString()
                        + " stable=" + mPendingStableInsets.toShortString()
                        + " cutout=" + mPendingDisplayCutout.get().toString()
                        + " outsets=" + mPendingOutsets.toShortString()
                        + " surface=" + mSurface);

                // If the pending {@link MergedConfiguration} handed back from
                // {@link #relayoutWindow} does not match the one last reported,
                // WindowManagerService has reported back a frame from a configuration not yet
                // handled by the client. In this case, we need to accept the configuration so we
                // do not lay out and draw with the wrong configuration.
                if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
                    if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
                            + mPendingMergedConfiguration.getMergedConfiguration());
                    performConfigurationChange(mPendingMergedConfiguration, !mFirst,
                            INVALID_DISPLAY /* same display */);
                    updatedConfiguration = true;
                }
                // mPendingOverscanInsets等rect在relayoutWindow方法里保存了最新窗口大小值
                // 再与上一次测量的保存在mAttachInfo中的值进行比较
                final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
                        mAttachInfo.mOverscanInsets);
                contentInsetsChanged = !mPendingContentInsets.equals(
                        mAttachInfo.mContentInsets);
                final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
                        mAttachInfo.mVisibleInsets);
                final boolean stableInsetsChanged = !mPendingStableInsets.equals(
                        mAttachInfo.mStableInsets);
                final boolean cutoutChanged = !mPendingDisplayCutout.equals(
                        mAttachInfo.mDisplayCutout);
                final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                surfaceSizeChanged = (relayoutResult
                        & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
                surfaceChanged |= surfaceSizeChanged;
                final boolean alwaysConsumeSystemBarsChanged =
                        mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
                final boolean colorModeChanged = hasColorModeChanged(lp.getColorMode());
                if (contentInsetsChanged) {
                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
                    if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
                            + mAttachInfo.mContentInsets);
                }
                // 如果发生了改变,就会进行重新赋值等操作
                if (overscanInsetsChanged) {
                    mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
                    if (DEBUG_LAYOUT) Log.v(mTag, "Overscan insets changing to: "
                            + mAttachInfo.mOverscanInsets);
                    // Need to relayout with content insets.
                    contentInsetsChanged = true;
                }
              ......

上面的代码我们可以发现参数如下

1. Activity窗口是第一次执行测量、布局和绘制操作,即ViewRoot类的成员变量mFirst的值等于true。
2. 前面得到的变量windowShouldResize的值等于true,即Activity窗口的大小的确是发生了变化。
3. 前面得到的变量insetsChanged的值等于true,即Activity窗口的内容区域边衬发生了变化。
4. Activity窗口的可见性发生了变化,即变量viewVisibilityChanged的值等于true。
5. Activity窗口的属性发生了变化,即变量params指向了一个WindowManager.LayoutParams对象。

只要满足上诉条件之一,就会调用relayoutWindow这个方法来进行测量大小,

4.我们接着看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, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
        if (mSurfaceControl.isValid()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            destroySurface();
        }

        mPendingAlwaysConsumeSystemBars =
                (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;

        if (restore) {
            params.restore();
        }

        if (mTranslator != null) {
            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
            mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
        }
        setFrame(mTmpFrame);
        mInsetsController.onStateChanged(mTempInsets);
        return relayoutResult;
    }

mWindowSession它引用了运行在WindowManagerService服务这一侧的一个Session对象,我们看一下实现

先找一下mWindowSession是哪里初始化的:

IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
 
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

mWindowSession = WindowManagerGlobal.getWindowSession();中先获取了WMS,然后调用WMS的openSession获取的mWindowSession对象,我们在看看openSession在WindowManagerService.java中

 @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }

我们最后可以看到mWindowSession是一个Session对象,这里ViewRootImpl是通过Session对象来与WMS进行通信的。

5.接着我们来看Session.java 中的relayout()方法

frameworks\base\services\core\java\com\android\server\wm\Session.java

    @Override
    public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
            Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
        if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                + Binder.getCallingPid());
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
        int res = mService.relayoutWindow(this, window, seq, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outBackdropFrame, cutout,
                mergedConfiguration, outSurfaceControl, outInsetsState);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                + Binder.getCallingPid());
        return res;
    }

其中

. ViewRoot类的成员变量mWindow,用来标志要计算的是哪一个Activity窗口的大小。

 . int requestedWidth, int requestedHeightActivity窗口的顶层视图经过测量后得到的宽度和高度

. viewFlags窗口的可见状态

.outFrame.outOverscanInsets.outContentInsets.都是输出参数

本文的主要探讨怎么去测量大小的,接着我们去看mService.relayoutWindow这个方法,在Session中可以看到如下,实际是调用到了WMS中的relayoutWindow方法

  final WindowManagerService mService;

6.接着我们去看WMS中的relayoutWindow方法

 public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
        requestedWidth=10;
        int result = 0;
        boolean configChanged;
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        long origId = Binder.clearCallingIdentity();
        final int displayId;
        synchronized (mGlobalLock) {
            //通过IWindow找到对应的WindowState,并且获取各种参数。
            final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            displayId = win.getDisplayId();
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight);
            }

            win.setFrameNumber(frameNumber);

            final DisplayContent dc = win.getDisplayContent();
            if (!dc.mWaitingForConfig) {
                win.finishSeamlessRotation(false /* timeout */);
            }

            int attrChanges = 0;
            int flagChanges = 0;
            if (attrs != null) {
                displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                // if they don't have the permission, mask out the status bar bits
                if (seq == win.mSeq) {
                    int systemUiVisibility = attrs.systemUiVisibility
                            | attrs.subtreeSystemUiVisibility;
                    if ((systemUiVisibility & DISABLE_MASK) != 0) {
                        if (!hasStatusBarPermission(pid, uid)) {
                            systemUiVisibility &= ~DISABLE_MASK;
                        }
                    }
                    win.mSystemUiVisibility = systemUiVisibility;
                }
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }

       .....
            //设置缩放
            win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);

            if (win.mAttrs.surfaceInsets.left != 0
                    || win.mAttrs.surfaceInsets.top != 0
                    || win.mAttrs.surfaceInsets.right != 0
                    || win.mAttrs.surfaceInsets.bottom != 0) {
                winAnimator.setOpaqueLocked(false);
            }

            final int oldVisibility = win.mViewVisibility;

            // If the window is becoming visible, visibleOrAdding may change which may in turn
         .....

            // 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.
            //当窗体出现了变更,需要重新设置DisplayContent的各种参数,销毁不用的Surface,重新计算层级,计算触点区域等等
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);

           ...
            if (focusMayChange) {
                //当发现Window可能发生变化,则重新计算窗口大小。
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }

           ......

        if (configChanged) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: sendNewConfiguration");
            sendNewConfiguration(displayId);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        Binder.restoreCallingIdentity(origId);
        return result;
    }

7.我们主要关注WMS.updateFocusedWindowLocked,

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }

可以发现调用的是mRoot.updateFocusedWindowLocked()方法,我们在看看,mRoot

RootWindowContainer mRoot;

​/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        mTopFocusedAppByProcess.clear();
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayContent dc = mChildren.get(i);
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
            final WindowState newFocus = dc.mCurrentFocus;
            if (newFocus != null) {
                final int pidOfNewFocus = newFocus.mSession.mPid;
                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mAppToken);
                }
                if (topFocusedDisplayId == INVALID_DISPLAY) {
                    topFocusedDisplayId = dc.getDisplayId();
                }
            } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
                // The top-most display that has a focused app should still be the top focused
                // display even when the app window is not ready yet (process not attached or
                // window not added yet).
                topFocusedDisplayId = dc.getDisplayId();
            }
        }
        if (topFocusedDisplayId == INVALID_DISPLAY) {
            topFocusedDisplayId = DEFAULT_DISPLAY;
        }
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
                    + topFocusedDisplayId);
        }
        return changed;
    }

可以看到RootWindowContainer中的方法会调到dc.updateFocusedWindowLocked()中,接着我们查看DisplayContent 中的updateFocusedWindowLocked()方法如下

 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
            int topFocusedDisplayId) {
        // 寻找焦点窗口
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        if (mCurrentFocus == newFocus) {
            // 焦点窗口没有变化时,返回false
            return false;
        }
        boolean imWindowChanged = false;
        final WindowState imWindow = mInputMethodWindow;
        // 更新IME target窗口
        if (imWindow != null) {
            final WindowState prevTarget = mInputMethodTarget;
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            imWindowChanged = prevTarget != newTarget;

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }
       // IME target窗口发生变化,重新获取一次焦点窗口
        if (imWindowChanged) {
            mWmService.mWindowsChanged = true;
            setLayoutNeeded();
            newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        }
        // 通知WMS焦点窗口发生变化
        if (mCurrentFocus != newFocus) {
            mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
        }

        if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
                + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
                + " Callers=" + Debug.getCallers(4));
        // 记录旧焦点窗口
        final WindowState oldFocus = mCurrentFocus;
        // 更新新焦点窗口
        mCurrentFocus = newFocus;
        从mLosingFocus移除
        mLosingFocus.remove(newFocus);

        if (newFocus != null) {
            mWinAddedSinceNullFocus.clear();
            mWinRemovedSinceNullFocus.clear();

            if (newFocus.canReceiveKeys()) {
                // Displaying a window implicitly causes dispatching to be unpaused.
                // This is to protect against bugs if someone pauses dispatching but
                // forgets to resume.
                newFocus.mToken.paused = false;
            }
        }
        // 通知DisplayPolicy焦点窗口发生变化,并接受返回结果
        int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
         // IME target窗口发生变化,且旧焦点窗口非输入法窗口时
        if (imWindowChanged && oldFocus != mInputMethodWindow) {
            // 根据mode, 进行layout或assign window layer操作
            // Focus of the input method window changed. Perform layout if needed.
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/,  updateInputWindows);
                focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
            } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                // Client will do the layout, but we need to assign layers
                // for handleNewWindowLocked() below.
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }
        // DislayPolicy返回结果 
        if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            // The change in focus caused us to need to do a layout.  Okay.
            setLayoutNeeded();
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/, updateInputWindows);
            } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
                mWmService.mRoot.performSurfacePlacement(false);
            }
        }
        // 向InputMonitor中设置焦点窗口
        if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
            // If we defer assigning layers, then the caller is responsible for doing this part.
            getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
        }
        // 为IME窗口进行调整
        adjustForImeIfNeeded();

        // We may need to schedule some toast windows to be removed. The toasts for an app that
        // does not have input focus are removed within a timeout to prevent apps to redress
        // other apps' UI.
        // Toast窗口
        scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);

        if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
            pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
        }
        return true;
    }

imWindowChanged 是判断输入法焦点是否一致,而窗体焦点则是通过不同的WindowState来判断。

.上面可以看出如果输入法焦点出现了变化,且当前的模式是UPDATE_FOCUS_PLACING_SURFACES(需要强制重绘)则要重新计算窗体大小,否则则直接做一次层级变化即可。

.不是UPDATE_FOCUS_WILL_ASSIGN_LAYERS模式,则则需要处理触点焦点边距。

接下来我们来看看performLayout(true /*initial*/, updateInputWindows);这个方法

 void performLayout(boolean initial, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
        try {
            performLayoutNoTrace(initial, updateInputWindows);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }
 private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
        if (!isLayoutNeeded()) {
            return;
        }
        clearLayoutNeeded();

        final int dw = mDisplayInfo.logicalWidth;
        final int dh = mDisplayInfo.logicalHeight;
        if (DEBUG_LAYOUT) {
            Slog.v(TAG, "-------------------------------------");
            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw
                    + " dh=" + dh);
        }
        //设置DisplayFrame
        //设置当前显示屏幕的大小,以及获取过扫描区域,还会判断当前手机屏幕是否支持刘海屏。这一切实际上都是由硬件回馈到DisplayService,我们再从中获取的信息。
        mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
        // TODO: Not sure if we really need to set the rotation here since we are updating from
        // the display info above...
        mDisplayFrames.mRotation = mRotation;
        //beginLayoutLw开始测量
        mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);

        int seq = mLayoutSeq + 1;
        if (seq < 0) seq = 0;
        mLayoutSeq = seq;

        // Used to indicate that we have processed the dream window and all additional windows are
        // behind it.
        mTmpWindow = null;
        mTmpInitial = initial;

        // First perform layout of any root windows (not attached to another window).
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

        // Used to indicate that we have processed the dream window and all additional attached
        // windows are behind it.
        mTmpWindow2 = mTmpWindow;
        mTmpWindow = null;

        // 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 */);

        // Window frames may have changed. Tell the input dispatcher about it.
        mInputMonitor.layoutInputConsumers(dw, dh);
        mInputMonitor.setUpdateInputWindowsNeededLw();
        if (updateInputWindows) {
            mInputMonitor.updateInputWindowsLw(false /*force*/);
        }

        mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
    }

我们来看看  mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);这个方法

​/frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

 /**
     * Called when layout of the windows is about to start.
     *
     * @param displayFrames frames of the display we are doing layout on.
     * @param uiMode The current uiMode in configuration.
     */
    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
        displayFrames.onBeginLayout();
        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();

        // For purposes of putting out fake window up to steal focus, we will
        // drive nav being hidden only by whether it is requested.
        final int sysui = mLastSystemUiFlags;
        boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
        boolean navTranslucent = (sysui
                & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
        boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
        boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        boolean navAllowedHidden = immersive || immersiveSticky;
        navTranslucent &= !immersiveSticky;  // transient trumps translucent
        boolean isKeyguardShowing = isStatusBarKeyguard()
                && !mService.mPolicy.isKeyguardOccluded();
        boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null
                && (mStatusBar.getAttrs().privateFlags
                & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;

        // When the navigation bar isn't visible, we put up a fake input window to catch all
        // touch events. This way we can detect when the user presses anywhere to bring back the
        // nav bar and ensure the application doesn't see the event.
        if (navVisible || navAllowedHidden) {
            if (mInputConsumer != null) {
                mHandler.sendMessage(
                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
                mInputConsumer = null;
            }
        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
            mInputConsumer = mService.createInputConsumer(mHandler.getLooper(),
                    INPUT_CONSUMER_NAVIGATION,
                    HideNavInputEventReceiver::new,
                    displayFrames.mDisplayId);
            // As long as mInputConsumer is active, hover events are not dispatched to the app
            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
        }

        // For purposes of positioning and showing the nav bar, if we have decided that it can't
        // be hidden (because of the screen aspect ratio), then take that into account.
        navVisible |= !canHideNavigationBar();

        boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
                navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);
        if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
        updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
        if (updateSysUiVisibility) {
            updateSystemUiVisibilityLw();
        }
        layoutScreenDecorWindows(displayFrames);

        if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
            // Make sure that the zone we're avoiding for the cutout is at least as tall as the
            // status bar; otherwise fullscreen apps will end up cutting halfway into the status
            // bar.
            displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
                    displayFrames.mStable.top);
        }

        // In case this is a virtual display, and the host display has insets that overlap this
        // virtual display, apply the insets of the overlapped area onto the current and content
        // frame of this virtual display. This let us layout windows in the virtual display as
        // expected when the window needs to avoid overlap with the system windows.
        // TODO: Generalize the forwarded insets, so that we can handle system windows other than
        // IME.
        displayFrames.mCurrent.inset(mForwardedInsets);
        displayFrames.mContent.inset(mForwardedInsets);
    }
  • 函数onBeginLayout,初始化了所有边距值
  • layoutNavigationBar ,测量NavigationBar
  • layoutStatusBar 测量layoutStatusBar
  • layoutScreenDecorWindows 测量所有装饰窗口

  DisplayFrames.onBeginLayout();

public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
        displayFrames.onBeginLayout();

  public void onBeginLayout() {
        switch (mRotation) {
            case ROTATION_90:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;
                break;
            case ROTATION_180:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;
                break;
            case ROTATION_270:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;
                break;
            default:
                mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);
                break;
        }

        mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);
        mOverscan.set(mRestrictedOverscan);
        mSystem.set(mRestrictedOverscan);
        mUnrestricted.set(mRotatedDisplayInfoOverscan);
        mUnrestricted.right = mDisplayWidth - mUnrestricted.right;
        mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;
        mRestricted.set(mUnrestricted);
        mDock.set(mUnrestricted);
        mContent.set(mUnrestricted);
        mVoiceContent.set(mUnrestricted);
        mStable.set(mUnrestricted);
        mStableFullscreen.set(mUnrestricted);
        mCurrent.set(mUnrestricted);

        mDisplayCutout = mDisplayInfoCutout;
        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
                Integer.MAX_VALUE, Integer.MAX_VALUE);
        if (!mDisplayCutout.getDisplayCutout().isEmpty()) {
            final DisplayCutout c = mDisplayCutout.getDisplayCutout();
            if (c.getSafeInsetLeft() > 0) {
                mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft();
            }
            if (c.getSafeInsetTop() > 0) {
                mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop();
            }
            if (c.getSafeInsetRight() > 0) {
                mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight();
            }
            if (c.getSafeInsetBottom() > 0) {
                mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom();
            }
        }
    }

可以看到所有的所有的间距将会设置为mUnrestricted的初始宽高,也就是不包含OverScan区域。如果是遇到刘海屏,则会根据设置的SafeInset区域来设置mDisplayCutoutSafe的安全区域。也就是我上面那种情况。

layoutNavigationBar测量NavigationBar

 boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
                navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);

        boolean navTranslucent, boolean navAllowedHidden,
            boolean statusBarForcesShowingNavigation) {
        if (mNavigationBar == null) {
            return false;
        }

        final Rect navigationFrame = sTmpNavFrame;
        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
        // Force the navigation bar to its appropriate place and size. We need to do this directly,
        // instead of relying on it to bubble up from the nav bar, because this needs to change
        // atomically with screen rotations.
        final int rotation = displayFrames.mRotation;
        final int displayHeight = displayFrames.mDisplayHeight;
        final int displayWidth = displayFrames.mDisplayWidth;
        final Rect dockFrame = displayFrames.mDock;
        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);

        final Rect cutoutSafeUnrestricted = sTmpRect;
        cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
        cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);

        if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
            // It's a system nav bar or a portrait screen; nav bar goes on bottom.
            final int top = cutoutSafeUnrestricted.bottom
                    - getNavigationBarHeight(rotation, uiMode);
            final int topNavBar = cutoutSafeUnrestricted.bottom
                    - getNavigationBarFrameHeight(rotation, uiMode);
            navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
            if (transientNavBarShowing) {
                mNavigationBarController.setBarShowingLw(true);
            } else if (navVisible) {
                mNavigationBarController.setBarShowingLw(true);
                dockFrame.bottom = displayFrames.mRestricted.bottom =
                        displayFrames.mRestrictedOverscan.bottom = top;
            } else {
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
            }
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) {
                // If the opaque nav bar is currently requested to be visible and not in the process
                // of animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.bottom = top;
            }
        } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
            // Landscape screen; nav bar goes to the right.
            final int left = cutoutSafeUnrestricted.right
                    - getNavigationBarWidth(rotation, uiMode);
            navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
            displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
            if (transientNavBarShowing) {
                mNavigationBarController.setBarShowingLw(true);
            } else if (navVisible) {
                mNavigationBarController.setBarShowingLw(true);
                dockFrame.right = displayFrames.mRestricted.right =
                        displayFrames.mRestrictedOverscan.right = left;
            } else {
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
            }
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) {
                // If the nav bar is currently requested to be visible, and not in the process of
                // animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.right = left;
            }
        } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
            // Seascape screen; nav bar goes to the left.
            final int right = cutoutSafeUnrestricted.left
                    + getNavigationBarWidth(rotation, uiMode);
            navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
            displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
            if (transientNavBarShowing) {
                mNavigationBarController.setBarShowingLw(true);
            } else if (navVisible) {
                mNavigationBarController.setBarShowingLw(true);
                dockFrame.left = displayFrames.mRestricted.left =
                        displayFrames.mRestrictedOverscan.left = right;
            } else {
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
            }
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) {
                // If the nav bar is currently requested to be visible, and not in the process of
                // animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.left = right;
            }
        }

        // Make sure the content and current rectangles are updated to account for the restrictions
        // from the navigation bar.
        displayFrames.mCurrent.set(dockFrame);
        displayFrames.mVoiceContent.set(dockFrame);
        displayFrames.mContent.set(dockFrame);
        // And compute the final frame.
        sTmpRect.setEmpty();
        mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
                navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
                displayFrames.mDisplayCutoutSafe /* contentFrame */,
                navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
                navigationFrame /* stableFrame */,
                displayFrames.mDisplayCutoutSafe /* outsetFrame */);
        mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
        mNavigationBar.computeFrameLw();
        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());

        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
        return mNavigationBarController.checkHiddenLw();
    }

layoutStatusBar 测量layoutStatusBar

updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);

private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
            boolean isKeyguardShowing) {
        // decide where the status bar goes ahead of time
        if (mStatusBar == null) {
            return false;
        }
        // apply any navigation bar insets
        sTmpRect.setEmpty();
        final WindowFrames windowFrames = mStatusBar.getWindowFrames();
        windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
                displayFrames.mUnrestricted /* displayFrame */,
                displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
                displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
                displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);

        // Let the status bar determine its size.
        mStatusBar.computeFrameLw();

        // For layout, the status bar is always at the top with our fixed height.
        displayFrames.mStable.top = displayFrames.mUnrestricted.top
                + mStatusBarHeightForRotation[displayFrames.mRotation];
        // Make sure the status bar covers the entire cutout height
        displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
                displayFrames.mDisplayCutoutSafe.top);

        // Tell the bar controller where the collapsed status bar content is
        sTmpRect.set(mStatusBar.getContentFrameLw());
        sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
        sTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
        sTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
        mStatusBarController.setContentFrame(sTmpRect);

        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
        boolean statusBarTranslucent = (sysui
                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;

        // If the status bar is hidden, we don't want to cause windows behind it to scroll.
        if (mStatusBar.isVisibleLw() && !statusBarTransient) {
            // Status bar may go away, so the screen area it occupies is available to apps but just
            // covering them when the status bar is visible.
            final Rect dockFrame = displayFrames.mDock;
            dockFrame.top = displayFrames.mStable.top;
            displayFrames.mContent.set(dockFrame);
            displayFrames.mVoiceContent.set(dockFrame);
            displayFrames.mCurrent.set(dockFrame);

            if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
                    "dock=%s content=%s cur=%s", dockFrame.toString(),
                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));

            if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()
                    && !mStatusBar.isAnimatingLw()) {

                // If the opaque status bar is currently requested to be visible, and not in the
                // process of animating on or off, then we can tell the app that it is covered by
                // it.
                displayFrames.mSystem.top = displayFrames.mStable.top;
            }
        }
        return mStatusBarController.checkHiddenLw();
    }

上面几个方法中 我们都会用到computeFrameLw方法,

 mStatusBar.computeFrameLw();

 mNavigationBar.computeFrameLw();

computeFrameLw计算每一个WindowState的对应Window的窗体大小。

我们在看看

8.DisplayContent.performLayoutNoTrace()中的

 // 首先测量那些没有绑定父窗口的窗口
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

       //测量那些绑定了父窗口的窗口
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);

我们来看看forAllWindows(mPerformLayout, true /* traverseTopToBottom */); 这个方法中mPerformLayout实现的接口,

  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 = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
                || w.isGoneForLayoutLw();

   。。。

        // 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();
            }
            if (w.mAttrs.type == TYPE_DREAM) {
                // Don't layout windows behind a dream, so that if it does stuff like hide
                // the status bar we won't get a bad transition when it goes away.
                mTmpWindow = w;
            }
            w.mLayoutNeeded = false;
            w.prelayout();
            final boolean firstLayout = !w.isLaidOut();
            getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
            w.mLayoutSeq = mLayoutSeq;

            // If this is the first layout, we need to initialize the last inset values as
            // otherwise we'd immediately cause an unnecessary resize.
            if (firstLayout) {
                w.updateLastInsetValues();
            }

            if (w.mAppToken != null) {
                w.mAppToken.layoutLetterbox(w);
            }

            if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrameLw()
                    + " mContainingFrame=" + w.getContainingFrame()
                    + " mDisplayFrame=" + w.getDisplayFrameLw());
        }
    };

可以看到当满足条件

if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached)

就会调用

getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);

我们在去DisplayPolicy.layoutWindowLw 方法中看看,

  public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
        // We've already done the navigation bar, status bar, and all screen decor windows. If the
        // status bar can receive input, we need to layout it again to accommodate for the IME
        // window.
        //不处理状态栏,NavigationBar,以及mScreenDecorWindows包含的装饰WindowState,因为在begin中已经处理了
        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
                || mScreenDecorWindows.contains(win)) {
            return;
        }
        final WindowManager.LayoutParams attrs = win.getAttrs();
        final boolean isDefaultDisplay = win.isDefaultDisplay();
        //获取type
        final int type = attrs.type;
        final int fl = PolicyControl.getWindowFlags(win, attrs);
        final int pfl = attrs.privateFlags;
        final int sim = attrs.softInputMode;
        final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
        final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);

        final WindowFrames windowFrames = win.getWindowFrames();

        windowFrames.setHasOutsets(false);
        sTmpLastParentFrame.set(windowFrames.mParentFrame);

        //初始化当前的区域
        final Rect pf = windowFrames.mParentFrame;
        final Rect df = windowFrames.mDisplayFrame;
        final Rect of = windowFrames.mOverscanFrame;
        final Rect cf = windowFrames.mContentFrame;
        final Rect vf = windowFrames.mVisibleFrame;
        final Rect dcf = windowFrames.mDecorFrame;
        final Rect sf = windowFrames.mStableFrame;
        dcf.setEmpty();
        windowFrames.setParentFrameWasClippedByDisplayCutout(false);
        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);

        final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null
                && mNavigationBar.isVisibleLw();

        final int adjust = sim & SOFT_INPUT_MASK_ADJUST;

        final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
                || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;

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

        sf.set(displayFrames.mStable);

        if (type == TYPE_INPUT_METHOD) {
            //如果当前是输入法区域,所有区域先设置为输入对应的mDock区域
            vf.set(displayFrames.mDock);
            cf.set(displayFrames.mDock);
            of.set(displayFrames.mDock);
            df.set(displayFrames.mDock);
            windowFrames.mParentFrame.set(displayFrames.mDock);
            // IM dock windows layout below the nav bar...
            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
            // ...with content insets above the nav bar
            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                // The status bar forces the navigation bar while it's visible. Make sure the IME
                // avoids the navigation bar in that case.
                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
                    pf.right = df.right = of.right = cf.right = vf.right =
                            displayFrames.mStable.right;
                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
                }
            }

            // In case the navigation bar is on the bottom, we use the frame height instead of the
            // regular height for the insets we send to the IME as we need some space to show
            // additional buttons in SystemUI when the IME is up.
            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
                final int rotation = displayFrames.mRotation;
                final int uimode = mService.mPolicy.getUiMode();
                final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode)
                        - getNavigationBarHeight(rotation, uimode);
                if (navHeightOffset > 0) {
                    cf.bottom -= navHeightOffset;
                    sf.bottom -= navHeightOffset;
                    vf.bottom -= navHeightOffset;
                    dcf.bottom -= navHeightOffset;
                }
            }

            // IM dock windows always go to the bottom of the screen.
            attrs.gravity = Gravity.BOTTOM;
        } else if (type == TYPE_VOICE_INTERACTION) {
            //测量声音窗口
            of.set(displayFrames.mUnrestricted);
            df.set(displayFrames.mUnrestricted);
            pf.set(displayFrames.mUnrestricted);
            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                cf.set(displayFrames.mDock);
            } else {
                cf.set(displayFrames.mContent);
            }
            if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                vf.set(displayFrames.mCurrent);
            } else {
                vf.set(cf);
            }
        } else if (type == TYPE_WALLPAPER) {
            //测量壁纸
            layoutWallpaper(displayFrames, pf, df, of, cf);
        } else if (win == mStatusBar) {
            //测量状态栏
            of.set(displayFrames.mUnrestricted);
            df.set(displayFrames.mUnrestricted);
            pf.set(displayFrames.mUnrestricted);
            cf.set(displayFrames.mStable);
            vf.set(displayFrames.mStable);

            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
                cf.bottom = displayFrames.mContent.bottom;
            } else {
                cf.bottom = displayFrames.mDock.bottom;
                vf.bottom = displayFrames.mContent.bottom;
            }
        } else {
            //测量内容型弹窗
            dcf.set(displayFrames.mSystem);
            final boolean inheritTranslucentDecor =
                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
            final boolean isAppWindow =
                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
            final boolean topAtRest =
                    win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
            if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
                    // Ensure policy decor includes status bar
                    dcf.top = displayFrames.mStable.top;
                }
                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
                    // Ensure policy decor includes navigation bar
                    dcf.bottom = displayFrames.mStable.bottom;
                    dcf.right = displayFrames.mStable.right;
                }
            }
.....
    }

从上面的过程我们可以看到,主要是对输入法,状态栏声,声音窗口,壁纸,测量内容型弹窗(如Activity,Dialog对应的窗口(PhoneWindow),)等等

.输入法的区域设置

  vf.set(displayFrames.mDock);
            cf.set(displayFrames.mDock);
            of.set(displayFrames.mDock);
            df.set(displayFrames.mDock);
            windowFrames.mParentFrame.set(displayFrames.mDock);
            // IM dock windows layout below the nav bar...
            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
            // ...with content insets above the nav bar
            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                // The status bar forces the navigation bar while it's visible. Make sure the IME
                // avoids the navigation bar in that case.
                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
                    pf.right = df.right = of.right = cf.right = vf.right =
                            displayFrames.mStable.right;
                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
                }
            }

            // In case the navigation bar is on the bottom, we use the frame height instead of the
            // regular height for the insets we send to the IME as we need some space to show
            // additional buttons in SystemUI when the IME is up.
            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
                final int rotation = displayFrames.mRotation;
                final int uimode = mService.mPolicy.getUiMode();
                final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode)
                        - getNavigationBarHeight(rotation, uimode);
                if (navHeightOffset > 0) {
                    cf.bottom -= navHeightOffset;
                    sf.bottom -= navHeightOffset;
                    vf.bottom -= navHeightOffset;
                    dcf.bottom -= navHeightOffset;
                }
            }

            // IM dock windows always go to the bottom of the screen.
            attrs.gravity = Gravity.BOTTOM;

.声音窗口区域设置

 of.set(displayFrames.mUnrestricted);
            df.set(displayFrames.mUnrestricted);
            pf.set(displayFrames.mUnrestricted);
            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                cf.set(displayFrames.mDock);
            } else {
                cf.set(displayFrames.mContent);
            }
            if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                vf.set(displayFrames.mCurrent);
            } else {
                vf.set(cf);
            }

.壁纸区域设置

//测量壁纸
            layoutWallpaper(displayFrames, pf, df, of, cf);

.测量状态栏区域设置

SOFT_INPUT_ADJUST_RESIZE,比如说打开了adjustPan,整个屏幕向上移动了。此时的内容区域就是键盘的底部,可见区域当然是就是原来的内容区域的底部。

 //测量状态栏
            of.set(displayFrames.mUnrestricted);
            df.set(displayFrames.mUnrestricted);
            pf.set(displayFrames.mUnrestricted);
            cf.set(displayFrames.mStable);
            vf.set(displayFrames.mStable);

            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
                cf.bottom = displayFrames.mContent.bottom;
            } else {
                cf.bottom = displayFrames.mDock.bottom;
                vf.bottom = displayFrames.mContent.bottom;
            }

.测量内容型弹窗区域设置(如Activity,Dialog对应的窗口(PhoneWindow),)等等

else if (win == mStatusBar) {
            //测量状态栏
            of.set(displayFrames.mUnrestricted);
            df.set(displayFrames.mUnrestricted);
            pf.set(displayFrames.mUnrestricted);
            cf.set(displayFrames.mStable);
            vf.set(displayFrames.mStable);

            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
                cf.bottom = displayFrames.mContent.bottom;
            } else {
                cf.bottom = displayFrames.mDock.bottom;
                vf.bottom = displayFrames.mContent.bottom;
            }
        } else {
            //测量内容型弹窗
            dcf.set(displayFrames.mSystem);
            final boolean inheritTranslucentDecor =
                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
            final boolean isAppWindow =
                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
            final boolean topAtRest =
                    win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
            if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {\
                //实际上就是我们常见的Activity,Dialog
                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
                    // Ensure policy decor includes status bar
                    dcf.top = displayFrames.mStable.top;
                }
                //如果隐藏了Nav Ba
                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
                    // Ensure policy decor includes navigation bar
                    dcf.bottom = displayFrames.mStable.bottom;
                    dcf.right = displayFrames.mStable.right;
                }
            }

            if (layoutInScreen && layoutInsetDecor) {
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                        + "): IN_SCREEN, INSET_DECOR");
                // This is the case for a normal activity window: we want it to cover all of the
                // screen space, and it can take care of moving its contents to account for screen
                // decorations that intrude into that space.
                if (attached != null) {
                    // If this window is attached to another, our display
                    // frame is the same as the one we are attached to.
                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
                            displayFrames);
                } else {
                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
                        // Status bar panels are the only windows who can go on top of the status
                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
                        // have the same privileges as the status bar itself.
                        //
                        // However, they should still dodge the navigation bar if it exists.

                        pf.left = df.left = of.left = hasNavBar
                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
                        pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
                        pf.right = df.right = of.right = hasNavBar
                                ? displayFrames.mRestricted.right
                                : displayFrames.mUnrestricted.right;
                        pf.bottom = df.bottom = of.bottom = hasNavBar
                                ? displayFrames.mRestricted.bottom
                                : displayFrames.mUnrestricted.bottom;

                        if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
                    } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
                        // Asking to layout into the overscan region, so give it that pure
                        // unrestricted area.
                        of.set(displayFrames.mOverscan);
                        df.set(displayFrames.mOverscan);
                        pf.set(displayFrames.mOverscan);
                    } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                            && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
                            || type == TYPE_VOLUME_OVERLAY
                            || type == TYPE_KEYGUARD_DIALOG)) {
                        // Asking for layout as if the nav bar is hidden, lets the application
                        // extend into the unrestricted overscan screen area. We only do this for
                        // application windows and certain system windows to ensure no window that
                        // can be above the nav bar can do this.
                        df.set(displayFrames.mOverscan);
                        pf.set(displayFrames.mOverscan);
                        // We need to tell the app about where the frame inside the overscan is, so
                        // it can inset its content by that amount -- it didn't ask to actually
                        // extend itself into the overscan region.
                        of.set(displayFrames.mUnrestricted);
                    } else {
                        df.set(displayFrames.mRestrictedOverscan);
                        pf.set(displayFrames.mRestrictedOverscan);
                        // We need to tell the app about where the frame inside the overscan
                        // is, so it can inset its content by that amount -- it didn't ask
                        // to actually extend itself into the overscan region.
                        of.set(displayFrames.mUnrestricted);
                    }

                    if ((fl & FLAG_FULLSCREEN) == 0) {
                        if (win.isVoiceInteraction()) {
                            cf.set(displayFrames.mVoiceContent);
                        } else {
                            // IME Insets are handled on the client for ADJUST_RESIZE in the new
                            // insets world
                            if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
                                    || adjust != SOFT_INPUT_ADJUST_RESIZE) {
                                cf.set(displayFrames.mDock);
                            } else {
                                cf.set(displayFrames.mContent);
                            }
                        }
                    } else {
                        // Full screen windows are always given a layout that is as if the status
                        // bar and other transient decors are gone. This is to avoid bad states when
                        // moving from a window that is not hiding the status bar to one that is.
                        cf.set(displayFrames.mRestricted);
                    }
                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                        vf.set(displayFrames.mCurrent);
                    } else {
                        vf.set(cf);
                    }
                }
            } else if (layoutInScreen || (sysUiFl
                    & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                        + "): IN_SCREEN");
                // A window that has requested to fill the entire screen just
                // gets everything, period.
                if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
                    cf.set(displayFrames.mUnrestricted);
                    of.set(displayFrames.mUnrestricted);
                    df.set(displayFrames.mUnrestricted);
                    pf.set(displayFrames.mUnrestricted);
                    if (hasNavBar) {
                        pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
                        pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
                        pf.bottom = df.bottom = of.bottom = cf.bottom =
                                displayFrames.mRestricted.bottom;
                    }
                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
                } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
                    // The navigation bar has Real Ultimate Power.
                    of.set(displayFrames.mUnrestricted);
                    df.set(displayFrames.mUnrestricted);
                    pf.set(displayFrames.mUnrestricted);
                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
                } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
                        && ((fl & FLAG_FULLSCREEN) != 0)) {
                    // Fullscreen secure system overlays get what they ask for. Screenshot region
                    // selection overlay should also expand to full screen.
                    cf.set(displayFrames.mOverscan);
                    of.set(displayFrames.mOverscan);
                    df.set(displayFrames.mOverscan);
                    pf.set(displayFrames.mOverscan);
                } else if (type == TYPE_BOOT_PROGRESS) {
                    // Boot progress screen always covers entire display.
                    cf.set(displayFrames.mOverscan);
                    of.set(displayFrames.mOverscan);
                    df.set(displayFrames.mOverscan);
                    pf.set(displayFrames.mOverscan);
                } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
                        && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
                    // Asking to layout into the overscan region, so give it that pure unrestricted
                    // area.
                    cf.set(displayFrames.mOverscan);
                    of.set(displayFrames.mOverscan);
                    df.set(displayFrames.mOverscan);
                    pf.set(displayFrames.mOverscan);
                } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                        && (type == TYPE_STATUS_BAR
                        || type == TYPE_TOAST
                        || type == TYPE_DOCK_DIVIDER
                        || type == TYPE_VOICE_INTERACTION_STARTING
                        || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
                    // Asking for layout as if the nav bar is hidden, lets the
                    // application extend into the unrestricted screen area.  We
                    // only do this for application windows (or toasts) to ensure no window that
                    // can be above the nav bar can do this.
                    // XXX This assumes that an app asking for this will also
                    // ask for layout in only content.  We can't currently figure out
                    // what the screen would be if only laying out to hide the nav bar.
                    cf.set(displayFrames.mUnrestricted);
                    of.set(displayFrames.mUnrestricted);
                    df.set(displayFrames.mUnrestricted);
                    pf.set(displayFrames.mUnrestricted);
                } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
                    of.set(displayFrames.mRestricted);
                    df.set(displayFrames.mRestricted);
                    pf.set(displayFrames.mRestricted);

                    // IME Insets are handled on the client for ADJUST_RESIZE in the new insets
                    // world
                    if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
                            || adjust != SOFT_INPUT_ADJUST_RESIZE) {
                        cf.set(displayFrames.mDock);
                    } else {
                        cf.set(displayFrames.mContent);
                    }
                } else {
                    cf.set(displayFrames.mRestricted);
                    of.set(displayFrames.mRestricted);
                    df.set(displayFrames.mRestricted);
                    pf.set(displayFrames.mRestricted);
                }

                applyStableConstraints(sysUiFl, fl, cf, displayFrames);

                if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                    vf.set(displayFrames.mCurrent);
                } else {
                    vf.set(cf);
                }
            } else if (attached != null) {
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                        + "): attached to " + attached);
                // A child window should be placed inside of the same visible
                // frame that its parent had.
                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
                        displayFrames);
            } else {
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                        + "): normal window");
                // Otherwise, a normal window must be placed inside the content
                // of all screen decorations.
                if (type == TYPE_STATUS_BAR_PANEL) {
                    // Status bar panels can go on
                    // top of the status bar. They are protected by the STATUS_BAR_SERVICE
                    // permission, so they have the same privileges as the status bar itself.
                    cf.set(displayFrames.mRestricted);
                    of.set(displayFrames.mRestricted);
                    df.set(displayFrames.mRestricted);
                    pf.set(displayFrames.mRestricted);
                } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
                    // These dialogs are stable to interim decor changes.
                    cf.set(displayFrames.mStable);
                    of.set(displayFrames.mStable);
                    df.set(displayFrames.mStable);
                    pf.set(displayFrames.mStable);
                } else {
                    pf.set(displayFrames.mContent);
                    if (win.isVoiceInteraction()) {
                        cf.set(displayFrames.mVoiceContent);
                        of.set(displayFrames.mVoiceContent);
                        df.set(displayFrames.mVoiceContent);
                    } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                        cf.set(displayFrames.mDock);
                        of.set(displayFrames.mDock);
                        df.set(displayFrames.mDock);
                    } else {
                        cf.set(displayFrames.mContent);
                        of.set(displayFrames.mContent);
                        df.set(displayFrames.mContent);
                    }
                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                        vf.set(displayFrames.mCurrent);
                    } else {
                        vf.set(cf);
                    }
                }
            }
        }

这段代码对应了很多场景,如隐藏了Nav Bar,

 //如果隐藏了Nav Ba
                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {

如果是我们的Activity,或者Dialog

/实际上就是我们常见的Activity,Dialog
                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_FULLSCREEN) == 0
                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
                    // Ensure policy decor includes status bar
                    dcf.top = displayFrames.mStable.top;
                }

绑定了父窗口,和没有绑定父窗口的

 if (layoutInScreen && layoutInsetDecor) {
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                        + "): IN_SCREEN, INSET_DECOR");
                // This is the case for a normal activity window: we want it to cover all of the
                // screen space, and it can take care of moving its contents to account for screen
                // decorations that intrude into that space.
                if (attached != null) {
                    // If this window is attached to another, our display
                    // frame is the same as the one we are attached to.
                    //测量那些绑定了父窗口的窗口
                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
                            displayFrames);
                } else {
                    //测量那些没有绑定父窗口的窗口
                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
                        // Status bar panels are the only windows who can go on top of the status
                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
                        // have the same privileges as the status bar itself.
                        //
                        // However, they should still dodge the navigation bar if it exists.
 

总结

最后我也来做个总结把,随着Android版本的不同,在不同的版本会对方法进行重构,但是经过我的排查,基本都是移动了位置,不会修改整体逻辑,只会增加,

DisplayPolicy.beginLayoutLw()

  • 测量Nav Bar
  • 测量status Bar
  • 测量layoutDecorScreen

而DisplayPolicy.layoutWindowLw()

测量所有窗体的显示屏区域,过扫描区域,父区域,内容区域,可见区域。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值