WMS相关学习-relayoutWindow(5)

前言

前面说到performTraversals会调用relayoutWindow;其请求WMS来计算相关的窗口大小,创建Surface等等;本章试着分析这个函数,就目前的理解performTraversals主要侧重于客户端对DecorView及其子View的measure,layout,draw等三大流程的处理;而binder call到system_server的WMS,是着重于系统侧对窗口的处理

ViewRootImpl#relayoutWindow

请求WindowManagerService服务计算Activity窗口的大小以及过扫描区域边衬大小和可见区域边衬大小(计算完毕之后,Activity窗口的大小就会保存在成员变量mWinFrame中,而Activity窗口的内容区域边衬大小和可见区域边衬大小分别保存在ViewRoot类的成员变量mPendingOverscanInsets和mPendingVisibleInsets中

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,
            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
            mPendingMergedConfiguration, mSurface);

    mPendingAlwaysConsumeNavBar =
            (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;

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

    if (mTranslator != null) {
        mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
        mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
        mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
        mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
        mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
    }
    return relayoutResult;
}

Session#relayout

调用WMS的relayoutWindow
mWindow 用来标志要计算的是哪一个Activity窗口的大小 2、Activity窗口的顶层视图经过客户端测量后得到的宽度和高度 3、Activity窗口的可见状态,即viewVisibility 4、Activity窗口是否有额外的内容区域边衬和可见区域边衬等待告诉给WindowManagerService服务,即参数insetsPending 5、mWinFrame,这是一个输出参数,用来保存WindowManagerService服务计算后得到的Activity窗口的大小 6、mPendingOverscanInsets用来保存Overscan边衬,mPendingContentInsets用来保存内容区域边衬,mPendingVisibleInsets用来保存可见区域边衬,mPendingStableInsets用来保存可能被系统UI元素部分或完全遮蔽的全屏窗口区域 7、mPendingConfiguration,这是一个输出参数,用来保存WindowManagerService服务返回来的Activity窗口的配置信息 8、mSurface,这是一个输出参数,用来保存WindowManagerService服务返回来的Activity窗口的绘图表面

@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,
        Surface outSurface) {
    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, outSurface);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
            + Binder.getCallingPid());
    return res;
}

WindowManagerService#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,
        Surface outSurface) {
    int result = 0;
    boolean configChanged;
    final boolean hasStatusBarPermission =
            mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
                    == PackageManager.PERMISSION_GRANTED;
    final boolean hasStatusBarServicePermission =
            mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
                    == PackageManager.PERMISSION_GRANTED;

    long origId = Binder.clearCallingIdentity();
    final int displayId;
    synchronized(mWindowMap) {
        WindowState win = windowForClientLocked(session, client, false); //得到保存的WindowState
        if (win == null) {
            return 0;
        }
        displayId = win.getDisplayId();

        WindowStateAnimator winAnimator = win.mWinAnimator;
        if (viewVisibility != View.GONE) {
           //参数requestedWidth和requestedHeight描述的是应用程序进程请求设置Activity窗口中的宽度和高度,它们会被记录在WindowState对象win的成员变量mRequestedWidth和mRequestedHeight中
            win.setRequestedSize(requestedWidth, requestedHeight); //为WindowState的mRequestedWidth和mRequestedHeight赋值;window的期望值
        }

        win.setFrameNumber(frameNumber); //为WindowState的mFrameNumber属性赋值
        int attrChanges = 0;
        int flagChanges = 0;
        if (attrs != null) {
            mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
            // 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) {
                        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.");
            }

            // Odd choice but less odd than embedding in copyFrom()
            if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
                    != 0) {
                attrs.x = win.mAttrs.x;
                attrs.y = win.mAttrs.y;
                attrs.width = win.mAttrs.width;
                attrs.height = win.mAttrs.height;
            }

            flagChanges = win.mAttrs.flags ^= attrs.flags;
            attrChanges = win.mAttrs.copyFrom(attrs);

            if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                    | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
                win.mLayoutNeeded = true;
            }
            if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                    || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
                win.mAppToken.checkKeyguardFlagsChanged();
            }
            if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
                    && (mAccessibilityController != null)
                    && (win.getDisplayId() == DEFAULT_DISPLAY)) {
                // No move or resize, but the controller checks for title changes as well
                mAccessibilityController.onSomeWindowResizedOrMovedLocked();
            }

            if ((flagChanges & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                updateNonSystemOverlayWindowsVisibilityIfNeeded(
                        win, win.mWinAnimator.getShown());
            }
        }

        if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
        winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
        win.mEnforceSizeCompat =
                (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
        if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
            winAnimator.mAlpha = attrs.alpha;
        }
        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
        // change the IME target.
        final boolean becameVisible =
                (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                        && viewVisibility == View.VISIBLE;
        boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                || becameVisible;
        final boolean isDefaultDisplay = win.isDefaultDisplay();
        boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
                || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                || (!win.mRelayoutCalled));
    /** mRelayoutCalled 的含义
     * This is set after IWindowSession.relayout() has been called at
     * least once for the window.  It allows us to detect the situation
     * where we don't yet have a surface, but should have one soon, so
     * we can give the window focus before waiting for the relayout.
     */

        boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
        wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
        if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
            winAnimator.mSurfaceController.setSecure(isSecureLocked(win));
        }

        win.mRelayoutCalled = true; 
        win.mInRelayout = true; //设置WindowState相关属性值

        win.mViewVisibility = viewVisibility;
        if (DEBUG_SCREEN_ON) {
            RuntimeException stack = new RuntimeException();
            stack.fillInStackTrace();
            Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
                    + " newVis=" + viewVisibility, stack);
        }

        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.
        final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                        || !win.mAppToken.isClientHidden());

        // If we are not currently running the exit animation, we need to see about starting
        // one.
        // We don't want to animate visibility of windows which are pending replacement.
        // In the case of activity relaunch child windows could request visibility changes as
        // they are detached from the main application window during the tear down process.
        // If we satisfied these visibility changes though, we would cause a visual glitch
        // hiding the window before it's replacement was available. So we just do nothing on
        // our side.
        // This must be called before the call to performSurfacePlacement.
        if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
            if (DEBUG_VISIBILITY) {
                Slog.i(TAG_WM,
                        "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit);
            }
            result |= RELAYOUT_RES_SURFACE_CHANGED;
            if (!win.mWillReplaceWindow) {
                focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay,
                        focusMayChange);
            }
        }

        // We may be deferring layout passes at the moment, but since the client is interested
        // in the new out values right now we need to force a layout.

       mWindowPlacerLocked.performSurfacePlacement(true /* force */);    
      //调用WindowSurfacePlacer.performSurfacePlacement()来计算Activity窗口的大小。计算完成之后,参数client所描述的Activity窗口的大小、内容区域边衬大小和可见区域边边衬大小就会分别保存在WindowState对象win的成员变量mCompatFrame、mOverscanInsets、mContentInsets、mVisibleInsets、mStableInsets、mOutsets

        if (shouldRelayout) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");

            result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);

            try {
                result = createSurfaceControl(outSurface, result, win, winAnimator);
            } catch (Exception e) {
                mInputMonitor.updateInputWindowsLw(true /*force*/);

                Slog.w(TAG_WM, "Exception thrown when creating surface for client "
                         + client + " (" + win.mAttrs.getTitle() + ")",
                         e);
                Binder.restoreCallingIdentity(origId);
                return 0;
            }
            if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
               /** WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME的含义
 * This is the first time the window is being drawn,
 * so the client must call drawingFinished() when done
 */
                focusMayChange = isDefaultDisplay; // focusMayChange 从名字的意义上来看,判断焦点是否可能发生变化
            }
            if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
                setInputMethodWindowLocked(win);
                imMayMove = true;
            }
            win.adjustStartingWindowFlags();
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        } else {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2");

            winAnimator.mEnterAnimationPending = false;
            winAnimator.mEnteringAnimation = false;

            if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
                // We already told the client to go invisible, but the message may not be
                // handled yet, or it might want to draw a last frame. If we already have a
                // surface, let the client use that, but don't create new surface at this point.
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
                winAnimator.mSurfaceController.getSurface(outSurface);
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            } else {
                if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);

                try {
                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                            + win.mAttrs.getTitle());
                    outSurface.release();
                } finally {
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                }
            }

            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }

        if (focusMayChange) {
            //System.out.println("Focus may change: " + win.mAttrs.getTitle());
            if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                    false /*updateInputWindows*/)) {
                imMayMove = false;
            }
            //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
        }

        // updateFocusedWindowLocked() already assigned layers so we only need to
        // reassign them at this point if the IM window state gets shuffled
        boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
        final DisplayContent dc = win.getDisplayContent();
        if (imMayMove) {
            dc.computeImeTarget(true /* updateImeTarget */);
            if (toBeDisplayed) {
                // Little hack here -- we -should- be able to rely on the function to return
                // true if the IME has moved and needs its layer recomputed. However, if the IME
                // was hidden and isn't actually moved in the list, its layer may be out of data
                // so we make sure to recompute it.
                dc.assignWindowLayers(false /* setLayoutNeeded */);
            }
        }

        if (wallpaperMayMove) {
            win.getDisplayContent().pendingLayoutChanges |=
                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
        }

        if (win.mAppToken != null) {
            mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
        }

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                "relayoutWindow: updateOrientationFromAppTokens");
       configChanged = updateOrientationFromAppTokensLocked(displayId);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

        if (toBeDisplayed && win.mIsWallpaper) {
            DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
            dc.mWallpaperController.updateWallpaperOffset(
                    win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
        }
        if (win.mAppToken != null) {
            win.mAppToken.updateReportedVisibilityLocked();
        }
        if (winAnimator.mReportSurfaceResized) {
            winAnimator.mReportSurfaceResized = false;
            result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
        }
        if (mPolicy.isNavBarForcedShownLw(win)) {
            result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR;
        }
        if (!win.isGoneForLayoutLw()) {
            win.mResizedWhileGone = false;
        }

        // We must always send the latest {@link MergedConfiguration}, regardless of whether we
        // have already reported it. The client might not have processed the previous value yet
        // and needs process it before handling the corresponding window frame. the variable
        // {@code mergedConfiguration} is an out parameter that will be passed back to the
        // client over IPC and checked there.
        // Note: in the cases where the window is tied to an activity, we should not send a
        // configuration update when the window has requested to be hidden. Doing so can lead
        // to the client erroneously accepting a configuration that would have otherwise caused
        // an activity restart. We instead hand back the last reported
        // {@link MergedConfiguration}.
        if (shouldRelayout) {
            win.getMergedConfiguration(mergedConfiguration);
        } else {
            win.getLastReportedMergedConfiguration(mergedConfiguration);
        }

        win.setLastReportedMergedConfiguration(mergedConfiguration);

        // Update the last inset values here because the values are sent back to the client.
        // The last inset values represent the last client state.
        win.updateLastInsetValues();
       
      //将WindowState对象win的成员变量mCompatFrame、mOverscanInsets、mContentInsets、mVisibleInsets、mStableInsets、mOutsets拷贝赋值对应变量中,以便可以返回给应用程序进程
        outFrame.set(win.mCompatFrame);
        outOverscanInsets.set(win.mOverscanInsets);
        outContentInsets.set(win.mContentInsets);
        win.mLastRelayoutContentInsets.set(win.mContentInsets);
        outVisibleInsets.set(win.mVisibleInsets);
        outStableInsets.set(win.mStableInsets);
        outCutout.set(win.mDisplayCutout.getDisplayCutout());
        outOutsets.set(win.mOutsets);
        outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
        if (localLOGV) Slog.v(
            TAG_WM, "Relayout given client " + client.asBinder()
            + ", requestedWidth=" + requestedWidth
            + ", requestedHeight=" + requestedHeight
            + ", viewVisibility=" + viewVisibility
            + "\nRelayout returning frame=" + outFrame
            + ", surface=" + outSurface);

        if (localLOGV || DEBUG_FOCUS) Slog.v(
            TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);

        result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;

        mInputMonitor.updateInputWindowsLw(true /*force*/);

        if (DEBUG_LAYOUT) {
            Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
        }
        win.mInRelayout = false;
    }

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

WindowState#setRequestedSize
将客户端计算出请求的宽和高的值保存下来

void setRequestedSize(int requestedWidth, int requestedHeight) {
        if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
            mLayoutNeeded = true;
            mRequestedWidth = requestedWidth;
            mRequestedHeight = requestedHeight;
        }
    }

WindowSurfacePlacer#performSurfacePlacement

final void performSurfacePlacement(boolean force) {
    if (mDeferDepth > 0 && !force) {
        return;
    }
    int loopCount = 6;
    do {
        mTraversalScheduled = false;
        performSurfacePlacementLoop();
        mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
        loopCount--;
    } while (mTraversalScheduled && loopCount > 0);
    mService.mRoot.mWallpaperActionPending = false;
}

WindowSurfacePlacer#performSurfacePlacementLoop

private void performSurfacePlacementLoop() {
    if (mInLayout) {
        if (DEBUG) {
            throw new RuntimeException("Recursive call!");
        }
        Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
                + Debug.getCallers(3));
        return;
    }

    if (mService.mWaitingForConfig) {
        // Our configuration has changed (most likely rotation), but we
        // don't yet have the complete configuration to report to
        // applications.  Don't do any window layout until we have it.
        return;
    }

    if (!mService.mDisplayReady) {
        // Not yet initialized, nothing to do.
        return;
    }

    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
    mInLayout = true; //mInLayout = true

    boolean recoveringMemory = false;
    if (!mService.mForceRemoves.isEmpty()) {
        recoveringMemory = true;
        // Wait a little bit for things to settle down, and off we go.
        while (!mService.mForceRemoves.isEmpty()) {
           //查系统中是否存在强制删除的窗口。有内存不足的情况下,有一些窗口就会被回收,即要从系统中删除,这些窗口会保存在WindowManagerService类的成员变量   mForceRemoves所描述的一个ArrayList中。如果存在这些窗口,那么performSurfacePlacementLoop就会调用WindowState.removeImmediately来删除它们,以便可以回收它们所占用的内存
            final WindowState ws = mService.mForceRemoves.remove(0);
            Slog.i(TAG, "Force removing: " + ws);
            ws.removeImmediately();
        }
        Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
        Object tmp = new Object();
        synchronized (tmp) {
            try {
                tmp.wait(250);
            } catch (InterruptedException e) {
            }
        }
    }

    try {
        mService.mRoot.performSurfacePlacement(recoveringMemory);

        mInLayout = false;

        if (mService.mRoot.isLayoutNeeded()) {
            if (++mLayoutRepeatCount < 6) {
                requestTraversal();
            } else {
                Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
                mLayoutRepeatCount = 0;
            }
        } else {
            mLayoutRepeatCount = 0;
        }

        if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
            mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
            mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
        }
    } catch (RuntimeException e) {
        mInLayout = false;
        Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
    }

    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}

RootWindowContainer#performSurfacePlacement

(一次UI刷新)

执行的操作绝非是计算窗口大小这么简单。计算窗口大小只是其中的一个小小功能点,它主要的功能是用来刷新系统的UI。在我们这个情景中,为什么需要刷新系统的UI呢?Activity窗口在其属性发生了变化,例如,可见性、大小发生了变化,又或者它新增、删除了子视图,都需要重新计算大小,而这些变化都是要求WindowManagerService服务重新刷新系统的UI的。事实上,刷新系统的UI是WindowManagerService服务的主要任务,在新增和删除了窗口、窗口动画显示过程、窗口切换过程中,WindowManagerService服务都需要不断地刷新系统的UI

参考 Android6.0 WMS(五) WMS计算Activity窗口大小的过程分析(二)WMS的relayoutWindow

  1. 在一个最多执行7次的while循环中,做两件事情:第一件事情是计算各个窗品的大小,这是通过调用另
    外一个成员函数performLayoutLockedInner来实现的;第二件事情是执行窗口的动画,主要是处理窗口的启动窗口显示动画和窗口切换过程中的动画,以及更新各个窗口的可见性。注意,每一次while循环执行之后,如果发现系统中的各个窗口的相应布局属性不再发生变化,那么就不行执行下一次的while循环了,即该while循环可能不用执行7次就结束了。窗口的动画显示过程和窗口的可见性更新过程是相当复杂的,它们也是WindowManagerService服务最为核的地方,在后面的文章中,我们再详细分析。

  2. 经过第1点的操作之后,接下来就可以将各个窗口的属性,例如,大小、位置等属性,通知SurfaceFlinger服务了,也就是让SurfaceFlinger服务更新它里面的各个Layer的属性值,以便可以对这些Layer执行可见性计算、合成等操作,最后渲染到硬件帧缓冲区中去。SurfaceFlinger服务计算系统中各个窗口,即各个Layer的可见性,以便将它们合成、渲染到硬件帧缓冲区。注意,各个窗口的属性更新操作是被包含在SurfaceFlinger服务的一个事务中的,即一个Transaction中,这样做是为了避免每更新一个窗口的一个属性就触发SurfaceFlinger服务重新计算各个Layer的可见性,以及对各个Layer进行合并和渲染的操作。启动SurfaceFlinger服务的一个事务可以通过调用Surface类的静态成员函数openTransaction来实现,而关闭SurfaceFlinger服务的一个事务可以通过调用Surface类的静态成员函数closeTransaction来实现。到SurfaceFlinger的各个Layer设置属性是在WindowState的WindowStateAnimator的setSurfaceBoundariesLocked函数中调用mSurfaceControl变量设置一些属性。

  3. 经过第1点和第2点的操作之后,一次系统UI的刷新过程就完成了,这时候就会将系统中的那些不会再显示的窗口的绘图表面销毁掉,并且将那些已经完成退出了的窗口令牌WindowToken移除掉,以及将那些已经退出了的Activity窗口令牌AppWindowToken也移除掉。这一步实际执行的是窗口清理操作。

// "Something has changed!  Let's make it correct now."
// TODO: Super crazy long method that should be broken down...
void performSurfacePlacement(boolean recoveringMemory) {
    if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
            + Debug.getCallers(3));

    int i;
    boolean updateInputWindowsNeeded = false;

    if (mService.mFocusMayChange) {
        mService.mFocusMayChange = false;
        updateInputWindowsNeeded = mService.updateFocusedWindowLocked(
                UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
    }

    // Initialize state of exiting tokens.
    final int numDisplays = mChildren.size();
    for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
        final DisplayContent displayContent = mChildren.get(displayNdx);
        displayContent.setExitingTokensHasVisible(false);
    }

    mHoldScreen = null;
    mScreenBrightness = -1;
    mUserActivityTimeout = -1;
    mObscureApplicationContentOnSecondaryDisplays = false;
    mSustainedPerformanceModeCurrent = false;
    mService.mTransactionSequence++;

    // TODO(multi-display):
    final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
    final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
    final int defaultDw = defaultInfo.logicalWidth;
    final int defaultDh = defaultInfo.logicalHeight;

    if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
            ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
    mService.openSurfaceTransaction();
    try {
        applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh); //计算窗口大小
    } catch (RuntimeException e) {
        Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    } finally {
        mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
    }

    mService.mAnimator.executeAfterPrepareSurfacesRunnables();

    final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;

    // If we are ready to perform an app transition, check through all of the app tokens to be
    // shown and see if they are ready to go.
    if (mService.mAppTransition.isReady()) {
        // This needs to be split into two expressions, as handleAppTransitionReadyLocked may
        // modify dc.pendingLayoutChanges, which would get lost when writing
        // defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked()
        final int layoutChanges = surfacePlacer.handleAppTransitionReadyLocked();
        defaultDisplay.pendingLayoutChanges |= layoutChanges;
        if (DEBUG_LAYOUT_REPEATS)
            surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked",
                    defaultDisplay.pendingLayoutChanges);
    }

    if (!isAppAnimating() && mService.mAppTransition.isRunning()) {
        // We have finished the animation of an app transition. To do this, we have delayed a
        // lot of operations like showing and hiding apps, moving apps in Z-order, etc. The app
        // token list reflects the correct Z-order, but the window list may now be out of sync
        // with it. So here we will just rebuild the entire app window list. Fun!
        defaultDisplay.pendingLayoutChanges |=
                mService.handleAnimatingStoppedAndTransitionLocked();
        if (DEBUG_LAYOUT_REPEATS)
            surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
                    defaultDisplay.pendingLayoutChanges);
    }

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

    if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
            && !mService.mAppTransition.isReady()) {
        // At this point, there was a window with a wallpaper that was force hiding other
        // windows behind it, but now it is going away. This may be simple -- just animate away
        // the wallpaper and its window -- or it may be hard -- the wallpaper now needs to be
        // shown behind something that was hidden.
        defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
        if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                "after animateAwayWallpaperLocked", defaultDisplay.pendingLayoutChanges);
    }
    mWallpaperForceHidingChanged = false;

    if (mWallpaperMayChange) {
        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
        defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
        if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
                defaultDisplay.pendingLayoutChanges);
    }

    if (mService.mFocusMayChange) {
        mService.mFocusMayChange = false;
        if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                false /*updateInputWindows*/)) {
            updateInputWindowsNeeded = true;
            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
        }
    }

    if (isLayoutNeeded()) {
        defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
        if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
                defaultDisplay.pendingLayoutChanges);
    }

    final ArraySet<DisplayContent> touchExcludeRegionUpdateDisplays = handleResizingWindows();

    if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
            "With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
    if (mOrientationChangeComplete) {
        if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
            mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
            mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
            mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
        }
        mService.stopFreezingDisplayLocked();
    }

    // Destroy the surface of any windows that are no longer visible.
    boolean wallpaperDestroyed = false;
    i = mService.mDestroySurface.size();
    if (i > 0) {
        do {
            i--;
            WindowState win = mService.mDestroySurface.get(i);
            win.mDestroying = false;
            if (mService.mInputMethodWindow == win) {
                mService.setInputMethodWindowLocked(null);
            }
            if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
                wallpaperDestroyed = true;
            }
            win.destroySurfaceUnchecked();
            win.mWinAnimator.destroyPreservedSurfaceLocked();
        } while (i > 0);
        mService.mDestroySurface.clear();
    }

    // Time to remove any exiting tokens?
    for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
        final DisplayContent displayContent = mChildren.get(displayNdx);
        displayContent.removeExistingTokensIfPossible();
    }

    if (wallpaperDestroyed) {
        defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
        defaultDisplay.setLayoutNeeded();
    }

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

    // Finally update all input windows now that the window changes have stabilized.
    mService.mInputMonitor.updateInputWindowsLw(true /*force*/);

    mService.setHoldScreenLocked(mHoldScreen);
    if (!mService.mDisplayFrozen) {
        final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
                ? -1 : toBrightnessOverride(mScreenBrightness);

        // Post these on a handler such that we don't call into power manager service while
        // holding the window manager lock to avoid lock contention with power manager lock.
        mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightness, 0).sendToTarget();
        mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
    }

    if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
        mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
        mService.mPowerManagerInternal.powerHint(
                PowerHint.SUSTAINED_PERFORMANCE,
                (mSustainedPerformanceModeEnabled ? 1 : 0));
    }

    if (mUpdateRotation) {
        if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
        // TODO(multi-display): Update rotation for different displays separately.
        final int displayId = defaultDisplay.getDisplayId();
        if (defaultDisplay.updateRotationUnchecked()) {
            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
        } else {
            mUpdateRotation = false;
        }
        // Update rotation of VR virtual display separately. Currently this is the only kind of
        // secondary display that can be rotated because of the single-display limitations in
        // PhoneWindowManager.
        final DisplayContent vrDisplay = mService.mVr2dDisplayId != INVALID_DISPLAY
                ? getDisplayContent(mService.mVr2dDisplayId) : null;
        if (vrDisplay != null && vrDisplay.updateRotationUnchecked()) {
            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mService.mVr2dDisplayId)
                    .sendToTarget();
        }
    }

    if (mService.mWaitingForDrawnCallback != null ||
            (mOrientationChangeComplete && !defaultDisplay.isLayoutNeeded()
                    && !mUpdateRotation)) {
        mService.checkDrawnWindowsLocked();
    }
   final int N = mService.mPendingRemove.size(); 
    if (N > 0) {
        if (mService.mPendingRemoveTmp.length < N) {
            mService.mPendingRemoveTmp = new WindowState[N+10];
        }
        mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
        mService.mPendingRemove.clear();
        ArrayList<DisplayContent> displayList = new ArrayList();
        for (i = 0; i < N; i++) {
            final WindowState w = mService.mPendingRemoveTmp[i];
            w.removeImmediately();
            final DisplayContent displayContent = w.getDisplayContent();
            if (displayContent != null && !displayList.contains(displayContent)) {
                displayList.add(displayContent);
            }
        }

        for (int j = displayList.size() - 1; j >= 0; --j) {
            final DisplayContent dc = displayList.get(j);
            dc.assignWindowLayers(true /*setLayoutNeeded*/); //重新计算z-order
        }
    }
//有窗口删除后,还需要重新刷新UI

    // Remove all deferred displays stacks, tasks, and activities.
    for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
        mChildren.get(displayNdx).checkCompleteDeferredRemoval();
    }

    if (updateInputWindowsNeeded) {
        mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
    }
    mService.setFocusTaskRegionLocked(null);
    if (touchExcludeRegionUpdateDisplays != null) {
        final DisplayContent focusedDc = mService.mFocusedApp != null
                ? mService.mFocusedApp.getDisplayContent() : null;
        for (DisplayContent dc : touchExcludeRegionUpdateDisplays) {
            // The focused DisplayContent was recalcuated in setFocusTaskRegionLocked
            if (focusedDc != dc) {
                dc.setTouchExcludeRegion(null /* focusedTask */);
            }
        }
    }

    // Check to see if we are now in a state where the screen should
    // be enabled, because the window obscured flags have changed.
    mService.enableScreenIfNeededLocked();

    mService.scheduleAnimationLocked();

    if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
            "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
}

可以看到这个函数的最后有针对mPendingRemove的操作

RootWindowContainer#applySurfaceChangesTransaction

private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw,
        int defaultDh) {
    mHoldScreenWindow = null;
    mObscuringWindow = null;

    // TODO(multi-display): Support these features on secondary screens.
    if (mService.mWatermark != null) {
        mService.mWatermark.positionSurface(defaultDw, defaultDh); //针对水印等等的处理
    }

    if (mService.mStrictModeFlash != null) {
        mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
    }
    if (mService.mCircularDisplayMask != null) {
        mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
                mService.getDefaultDisplayRotation());
    }
    if (mService.mEmulatorDisplayOverlay != null) {
        mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
                mService.getDefaultDisplayRotation());
    }

    boolean focusDisplayed = false;

    final int count = mChildren.size();
    for (int j = 0; j < count; ++j) {
        final DisplayContent dc = mChildren.get(j);
        focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
    }

    if (focusDisplayed) {
        mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
    }

    // Give the display manager a chance to adjust properties like display rotation if it needs
    // to.
    mService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
    SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}

DisplayContent#applySurfaceChangesTransaction

// TODO: Super crazy long method that should be broken down...
boolean applySurfaceChangesTransaction(boolean recoveringMemory) {

    final int dw = mDisplayInfo.logicalWidth;
    final int dh = mDisplayInfo.logicalHeight;
    final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;

    mTmpUpdateAllDrawn.clear();

    int repeats = 0;
    do {
        repeats++;
        if (repeats > 6) {
            Slog.w(TAG, "Animation repeat aborted after too many iterations");
            clearLayoutNeeded();
            break;
        }

        if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
                pendingLayoutChanges);

        // TODO(multi-display): For now adjusting wallpaper only on primary display to avoid
        // the wallpaper window jumping across displays.
        // Remove check for default display when there will be support for multiple wallpaper
        // targets (on different displays).
        if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
            mWallpaperController.adjustWallpaperWindows(this);
        }

        if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
            if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
            if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
                setLayoutNeeded();
                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
            }
        }

        if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            setLayoutNeeded();
        }

        // FIRST LOOP: Perform a layout, if needed.
        if (repeats < LAYOUT_REPEAT_THRESHOLD) {
            performLayout(repeats == 1, false /* updateInputWindows */);
        } else {
            Slog.w(TAG, "Layout repeat skipped after too many iterations");
        }

        // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
        pendingLayoutChanges = 0;

        if (isDefaultDisplay) {
            mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
            forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
            pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw();
            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                    "after finishPostLayoutPolicyLw", pendingLayoutChanges);
        }
    } while (pendingLayoutChanges != 0);

    mTmpApplySurfaceChangesTransactionState.reset();

    mTmpRecoveringMemory = recoveringMemory;
    forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
    prepareSurfaces();

    mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
            mTmpApplySurfaceChangesTransactionState.displayHasContent,
            mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
            mTmpApplySurfaceChangesTransactionState.preferredModeId,
            true /* inTraversal, must call performTraversalInTrans... below */);

    final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
    if (wallpaperVisible != mLastWallpaperVisible) {
        mLastWallpaperVisible = wallpaperVisible;
        mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
    }

    while (!mTmpUpdateAllDrawn.isEmpty()) {
        final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
        // See if any windows have been drawn, so they (and others associated with them)
        // can now be shown.
        atoken.updateAllDrawn();
    }

    return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
}
DisplayContent#performLayout
  1. 准备阶段:调用PhoneWindowManager类的成员函数beginLayoutLw来设置屏幕的大小;包括状态栏,导航栏
  2. 计算阶段:调用forAllWindows分别计算父窗口和子窗口的大小。
  3. 结束阶段:调用PhoneWindowManager类的成员函数finishLayoutLw来执行一些清理工作
void performLayout(boolean initial, boolean updateInputWindows) {
   //第一次performLayout  initial = true
    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);
    }

    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;
    mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
   //mDisplayFrames维护整个屏幕的大小属性
    if (isDefaultDisplay) {
        // Not needed on non-default displays.
        mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
        mService.mScreenRect.set(0, 0, dw, dh);
    }

    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 */); //针对子窗口的计算(forAllWindows就和dump顺序一样,从上到下)

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

    mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
PhoneWindowManager#beginLayoutLw
/** {@inheritDoc} */
@Override
public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
    displayFrames.onBeginLayout(); //初始化DisplayFrames的属性值
    // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
    mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
    mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
    mDockLayer = 0x10000000;
    mStatusBarLayer = -1;

    // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
    final Rect pf = mTmpParentFrame;
    final Rect df = mTmpDisplayFrame;
    final Rect of = mTmpOverscanFrame;
    final Rect vf = mTmpVisibleFrame;
    final Rect dcf = mTmpDecorFrame;
    vf.set(displayFrames.mDock);
    of.set(displayFrames.mDock);
    df.set(displayFrames.mDock);
    pf.set(displayFrames.mDock);
    dcf.setEmpty();  // Decor frame N/A for system bars.

    if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
        // 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() && !mKeyguardOccluded;
        if (!isKeyguardShowing) {
            navTranslucent &= areTranslucentBarsAllowed();
        }
        boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null
                && mStatusBar.getAttrs().height == MATCH_PARENT
                && mStatusBar.getAttrs().width == MATCH_PARENT;

        // 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 = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
                    INPUT_CONSUMER_NAVIGATION,
                    (channel, looper) -> new HideNavInputEventReceiver(channel, looper));
            // 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, dcf,
                navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard); //计算导航栏
        if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
        updateSysUiVisibility |= layoutStatusBar(
                displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing); //计算状态栏
        if (updateSysUiVisibility) {
            updateSystemUiVisibilityLw();
        }
    }
    layoutScreenDecorWindows(displayFrames, pf, df, dcf);

    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. 
       (针对status bar的高度,重新设置mDisplayCutoutSafe.top)
        /** mDisplayCutoutSafe
 * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
 */
        displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
                displayFrames.mStable.top); 
    }
}
PhoneWindowManager#layoutNavigationBar
private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
        boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
        boolean statusBarExpandedNotKeyguard) {
    if (mNavigationBar == null) {
        return false;
    }
    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 = mTmpRect;
    cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
    cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);

    if (mNavigationBarPosition == NAV_BAR_BOTTOM) { //Navigation Bar在底部
        // It's a system nav bar or a portrait screen; nav bar goes on bottom.
        final int top = cutoutSafeUnrestricted.bottom
                - getNavigationBarHeight(rotation, uiMode);
        mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); //设置mTmpNavigationFrame这个Rect的值
        displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; //更新displayFrames中mStable,mStableFullscreen的底部值 ( displayFrames好像是包含当前display各模块的大小的一个汇总变量)
        //displayFrames
     
       /** For applications requesting stable content insets, these are them. */
public final Rect mStable = new Rect();

/**
 * For applications requesting stable content insets but have also set the fullscreen window
 * flag, these are the stable dimensions without the status bar.
 */
 public final Rect mStableFullscreen = new Rect();
*/
        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(statusBarExpandedNotKeyguard);
        }
        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);
        mTmpNavigationFrame.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(statusBarExpandedNotKeyguard);
        }
        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);
        mTmpNavigationFrame.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(statusBarExpandedNotKeyguard);
        }
        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);
    mStatusBarLayer = mNavigationBar.getSurfaceLayer();
    //根据导航栏的高度更新displayFrames中的一些变量(displayFrames是整个屏幕的大小的总结)
    // And compute the final frame.
   //下面是计算相应窗口的最终大小 computeFrameLw
    mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
            mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
            mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
            displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
    mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());

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

computeFrameLw是用来真正计算每个窗口的大小的函数,为WindowState的相关frame属性赋值,会在下一章中继续说明

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容