WindowManagerService的relayoutWindow方法源码简单的认识

WindowManagerService的relayoutWindow方法源码如下:
源码的路径:
https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java?hl=zh-cn%20addWindow%20%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E2%80%94%20%E7%89%88%E6%9D%83%E5%A3%B0%E6%98%8E%EF%BC%9A%E6%9C%AC%E6%96%87%E4%B8%BA%E5%8D%9A%E4%B8%BB%E5%8E%9F%E5%88%9B%E6%96%87%E7%AB%A0%EF%BC%8C%E9%81%B5%E5%BE%AA%20CC%204.0%20BY-SA%20%E7%89%88%E6%9D%83%E5%8D%8F%E8%AE%AE%EF%BC%8C%E8%BD%AC%E8%BD%BD%E8%AF%B7%E9%99%84%E4%B8%8A%E5%8E%9F%E6%96%87%E5%87%BA%E5%A4%84%E9%93%BE%E6%8E%A5%E5%92%8C%E6%9C%AC%E5%A3%B0%E6%98%8E%E3%80%82%20%E5%8E%9F%E6%96%87%E9%93%BE%E6%8E%A5%EF%BC%9Ahttps%2F%2F:%2F%2F%2F%2Fblog.csdn.net%2Fweixin_43228946%2Farticle%2Fdetails%2F136382100

public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
            int lastSyncSeqId, ClientWindowFrames outFrames,
            MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
            InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
            Bundle outSyncIdBundle) {
        if (outActiveControls != null) {
            outActiveControls.set(null);
        }
        int result = 0;
        boolean configChanged = false;
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        synchronized (mGlobalLock) {
            final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            if (win.mRelayoutSeq < seq) {
                win.mRelayoutSeq = seq;
            } else if (win.mRelayoutSeq > seq) {
                return 0;
            }

            if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) {
                // The client has reported the sync draw, but we haven't finished it yet.
                // Don't let the client perform a non-sync draw at this time.
                result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
            }

            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

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

            int attrChanges = 0;
            int flagChanges = 0;
            int privateFlagChanges = 0;
            if (attrs != null) {
                displayPolicy.adjustWindowParamsLw(win, attrs);
                attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
                attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), uid,
                        pid);
                int disableFlags =
                        (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
                if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
                    disableFlags = 0;
                }
                win.mDisableFlags = disableFlags;
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }
                if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) {
                    if (win.mAttrs.providedInsets == null || attrs.providedInsets == null
                            || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) {
                        throw new IllegalArgumentException(
                                "Insets amount can not be changed after the window is added.");
                    } else {
                        final int insetsTypes = attrs.providedInsets.length;
                        for (int i = 0; i < insetsTypes; i++) {
                            if (!win.mAttrs.providedInsets[i].idEquals(attrs.providedInsets[i])) {
                                throw new IllegalArgumentException(
                                        "Insets ID can not be changed after the window is added.");
                            }
                            final InsetsFrameProvider.InsetsSizeOverride[] overrides =
                                    win.mAttrs.providedInsets[i].getInsetsSizeOverrides();
                            final InsetsFrameProvider.InsetsSizeOverride[] newOverrides =
                                    attrs.providedInsets[i].getInsetsSizeOverrides();
                            if (!(overrides == null && newOverrides == null)) {
                                if (overrides == null || newOverrides == null
                                        || (overrides.length != newOverrides.length)) {
                                    throw new IllegalArgumentException(
                                            "Insets override types can not be changed after the "
                                                    + "window is added.");
                                } else {
                                    final int overrideTypes = overrides.length;
                                    for (int j = 0; j < overrideTypes; j++) {
                                        if (overrides[j].getWindowType()
                                                != newOverrides[j].getWindowType()) {
                                            throw new IllegalArgumentException(
                                                    "Insets override types can not be changed after"
                                                            + " the window is added.");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                flagChanges = win.mAttrs.flags ^ attrs.flags;
                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                final boolean layoutChanged =
                        (attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0;
                if (layoutChanged || (attrChanges
                        & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
                    win.mLayoutNeeded = true;
                }
                if (layoutChanged && win.providesDisplayDecorInsets()) {
                    configChanged = displayPolicy.updateDecorInsetsInfo();
                }
                if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                        || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
                    win.mActivityRecord.checkKeyguardFlagsChanged();
                }

                if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                    updateNonSystemOverlayWindowsVisibilityIfNeeded(
                            win, win.mWinAnimator.getShown());
                }
                if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) {
                    winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
                            & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
                }
                // See if the DisplayWindowPolicyController wants to keep the activity on the window
                if (displayContent.mDwpcHelper.hasController()
                        && win.mActivityRecord != null && (!win.mRelayoutCalled || flagChanges != 0
                        || privateFlagChanges != 0)) {
                    int newOrChangedFlags = !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges;
                    int newOrChangedPrivateFlags =
                            !win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges;

                    if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
                            win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags,
                            win.mAttrs.flags,
                            win.mAttrs.privateFlags)) {
                        mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
                                win.mActivityRecord.getTask()));
                        Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
                                + " can't remain on display " + displayContent.getDisplayId());
                        return 0;
                    }
                }
            }

            if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                    + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
            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;
            boolean focusMayChange = win.mViewVisibility != viewVisibility
                    || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                    || (!win.mRelayoutCalled);

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

            final boolean wasVisible = win.isVisible();

            win.mRelayoutCalled = true;
            win.mInRelayout = true;

            win.setViewVisibility(viewVisibility);
            ProtoLog.i(WM_DEBUG_SCREEN_ON,
                    "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                            viewVisibility, new RuntimeException().fillInStackTrace());

            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.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());

            // If we are not currently running the exit animation, we need to see about starting
            // one.
            // 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;
                // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag
                // in DC#pendingLayoutChanges and update the wallpaper target later.
                // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window
                // when the window is about to exit, so we update the wallpaper target
                // immediately here. Otherwise this window will be stuck in exiting and its
                // surface remains on the screen.
                // TODO(b/189856716): Allow destroying surface even if it belongs to the
                //  keyguard target.
                if (wallpaperMayMove) {
                    displayContent.mWallpaperController.adjustWallpaperWindows();
                }
                tryStartExitingAnimation(win, winAnimator);
            }

            // Create surfaceControl before surface placement otherwise layout will be skipped
            // (because WS.isGoneForLayout() is true when there is no surface.
            if (shouldRelayout && outSurfaceControl != null) {
                try {
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);

                    ProtoLog.w(WM_ERROR,
                            "Exception thrown when creating surface for client %s (%s). %s",
                            client, win.mAttrs.getTitle(), e);
                    Binder.restoreCallingIdentity(origId);
                    return 0;
                }
            }

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

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

                result = win.relayoutVisibleWindow(result);

                if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    focusMayChange = true;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.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 (outSurfaceControl != null) {
                    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.getSurfaceControl(outSurfaceControl);
                        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());
                            outSurfaceControl.release();
                        } finally {
                            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                        }
                    }
                }

                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }

            if (focusMayChange) {
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }

            // 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;
            if (imMayMove) {
                displayContent.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.
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                }
            }

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

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

            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
            configChanged |= displayContent.updateOrientation();
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

            if (toBeDisplayed && win.mIsWallpaper) {
                displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
            }
            if (win.mActivityRecord != null) {
                win.mActivityRecord.updateReportedVisibilityLocked();
            }
            if (displayPolicy.areSystemBarsForcedConsumedLw()) {
                result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
            }
            if (!win.isGoneForLayout()) {
                win.mResizedWhileGone = false;
            }

            if (outFrames != null && outMergedConfiguration != null) {
                win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
                        false /* useLatestConfig */, shouldRelayout);

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

            if (outInsetsState != null) {
                outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
            }

            ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                    win, focusMayChange);

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

            final boolean winVisibleChanged = win.isVisible() != wasVisible;
            if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
                dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type,
                        win.isVisible(), false /* removed */);
            }
            // Notify listeners about IME input target window visibility change.
            final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
            if (isImeInputTarget && winVisibleChanged) {
                dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
                        win.isVisible() /* visible */, false /* removed */);
            }

            if (outSyncIdBundle != null) {
                final int maybeSyncSeqId;
                if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
                        && win.mSyncSeqId > lastSyncSeqId) {
                    maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
                    win.markRedrawForSyncReported();
                } else {
                    maybeSyncSeqId = -1;
                }
                outSyncIdBundle.putInt("seqid", maybeSyncSeqId);
            }

            if (configChanged) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                        "relayoutWindow: postNewConfigurationToHandler");
                displayContent.sendNewConfiguration();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
            if (outActiveControls != null) {
                getInsetsSourceControls(win, outActiveControls);
            }
        }

        Binder.restoreCallingIdentity(origId);
        return result;
    }

relayoutWindow的基本逻辑和作用如下:

  • relayoutWindow:这是一个 Android 平台的函数,用于重新布局窗口,调整窗口的大小和位置。
  • 基本逻辑:relayoutWindow 函数首先检查窗口的可见性,然后根据窗口的属性和配置计算出新的窗口框架,接着更新窗口的状态和表面,最后通知窗口管理器和应用程序关于窗口的变化。
  • 作用:relayoutWindow 函数的作用是响应窗口的变化,例如旋转屏幕,改变分辨率,切换应用程序等,以保证窗口的正确显示和交互。

详细解释relayoutWindow方法

public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
            int lastSyncSeqId, ClientWindowFrames outFrames,
            MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
            InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
            Bundle outSyncIdBundle)
  • relayoutWindow 的参数:relayoutWindow 函数有 15 个参数,分别是:
    • session:表示调用该函数的会话对象,用于标识客户端进程。
    • client:表示要重新布局的窗口对象,用于标识具体的窗口。
    • attrs:表示窗口的布局属性,用于指定窗口的类型、位置、大小、动画等。
    • requestedWidth 和 requestedHeight:表示客户端请求的窗口宽度和高度,用于指定窗口的期望尺寸。
    • viewVisibility:表示窗口的可见性,用于指定窗口是否可见或隐藏。
    • flags:表示窗口的布局标志,用于指定窗口的一些选项,如是否需要计算边距、是否需要更新表面等。
    • seq:表示窗口的序列号,用于标识窗口的状态变化。
    • lastSyncSeqId:表示窗口的同步序列号,用于标识窗口的同步操作。
    • outFrames:表示窗口的各种区域,用于保存窗口的框架、内容、可见、稳定、裁剪等区域。
    • outMergedConfiguration:表示窗口的配置信息,用于保存窗口的全局和窗口配置。
    • outSurfaceControl:表示窗口的表面控制对象,用于保存窗口的绘图表面。
    • outInsetsState:表示窗口的内边距状态,用于保存窗口的各种内边距源的状态。
    • outActiveControls:表示窗口的内边距控制对象,用于保存窗口的各种内边距源的控制器。
    • outSyncIdBundle:表示窗口的同步标识对象,用于保存窗口的同步标识和同步序列号。
  • relayoutWindow 的返回值:relayoutWindow 函数返回一个整数值,表示窗口的布局结果,包括窗口的可见性、焦点、表面、内边距等信息。
  • relayoutWindow 的作用:relayoutWindow 函数的作用是响应窗口的变化,例如旋转屏幕,改变分辨率,切换应用程序等,以保证窗口的正确显示和交互。relayoutWindow 函数首先检查窗口的可见性,然后根据窗口的属性和配置计算出新的窗口框架,接着更新窗口的状态和表面,最后通知窗口管理器和应用程序关于窗口的变化。
if (outActiveControls != null) {
            outActiveControls.set(null);
        }
  • outActiveControls:这是 relayoutWindow 函数的一个参数,表示窗口的内边距控制对象,用于保存窗口的各种内边距源的控制器。内边距源是指影响窗口内边距的一些因素,如状态栏、导航栏、输入法等。内边距控制器是指可以控制内边距源的显示和隐藏的一些对象,如按钮、手势等。¹²
  • outActiveControls.set(null):这是一行代码,用于将 outActiveControls 参数设置为 null。这意味着 relayoutWindow 函数不会返回任何内边距控制对象给客户端,也就是说,客户端无法控制窗口的内边距源的显示和隐藏。这可能是因为 relayoutWindow 函数不需要或不支持返回内边距控制对象,或者是为了保证窗口的安全性和稳定性。
int result = 0;
        boolean configChanged = false;
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();

这段代码的逻辑和作用是:

  • 定义一个整型变量 result,并初始化为 0。这个变量用于存储某个方法的返回值或者表示操作的结果。
  • 定义一个布尔型变量 configChanged,并初始化为 false。这个变量可能用于标记配置是否发生了变化,以便后续的处理。
  • 调用 Binder.getCallingPid() 方法,获取当前进程的 ID,并赋值给一个常量 pid。这个方法是 Android Binder 类的一个静态方法,用于获取发送当前事务的进程的 ID。
  • 调用 Binder.getCallingUid() 方法,获取当前进程的用户 ID,并赋值给一个常量 uid。这个方法是 Android Binder 类的一个静态方法,用于获取发送当前事务的进程的用户 ID。
  • 调用 Binder.clearCallingIdentity() 方法,清除当前线程的 IPC 身份,并返回一个长整型常量 origId。这个方法是 Android Binder 类的一个静态方法,用于在跨进程调用时,暂时放弃当前进程的身份,以便使用系统服务或者其他应用的接口。
 synchronized (mGlobalLock)

synchronized (mGlobalLock) 同步代码块,保证只有一个线程能够执行该代码块,避免并发问题。mGlobalLock 是一个对象,用于作为同步代码块的锁。

final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            if (win.mRelayoutSeq < seq) {
                win.mRelayoutSeq = seq;
            } else if (win.mRelayoutSeq > seq) {
                return 0;
            }

            if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) {
                // The client has reported the sync draw, but we haven't finished it yet.
                // Don't let the client perform a non-sync draw at this time.
                result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
            }
  • 调用 windowForClientLocked(session, client, false) 方法,根据给定的会话和客户端,获取对应的窗口对象,并赋值给一个变量 win。这个方法是 WindowManagerService 类的一个私有方法,用于在锁定状态下,查找指定的窗口¹²。
  • 判断 win 是否为 null,如果是,则返回 0,表示没有找到窗口,结束方法。
  • 判断 win.mRelayoutSeqseq 的大小关系,其中 win.mRelayoutSeq 是窗口的重新布局序号,seq 是当前的重新布局序号。如果 win.mRelayoutSeq 小于 seq,则将 win.mRelayoutSeq 赋值为 seq,表示窗口需要重新布局。如果 win.mRelayoutSeq 大于 seq,则返回 0,表示窗口不需要重新布局,结束方法。
  • 调用 win.cancelAndRedraw() 方法,判断窗口是否需要取消并重绘。这个方法是 WindowState 类的一个公有方法,用于检查窗口是否有未完成的同步绘制请求。如果返回 true,则表示窗口需要取消并重绘,并且判断 win.mPrepareSyncSeqId 是否小于等于 lastSyncSeqId,其中 win.mPrepareSyncSeqId 是窗口的同步绘制序号,lastSyncSeqId 是上一次的同步绘制序号。如果条件成立,则表示客户端已经报告了同步绘制,但是服务端还没有完成,此时不应该让客户端执行非同步绘制。
  • 如果上述条件都成立,则将 resultRELAYOUT_RES_CANCEL_AND_REDRAW 做按位或运算,其中 result 是一个整型变量,用于存储重新布局的结果,RELAYOUT_RES_CANCEL_AND_REDRAW 是一个常量,表示需要取消并重绘的标志位。

总结: 这段代码是用于处理窗口的重新布局的,它使用了私有方法、公有方法、变量和常量,以及判断和运算逻辑。

final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

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

            int attrChanges = 0;
            int flagChanges = 0;
            int privateFlagChanges = 0;

用于处理窗口的显示内容、显示策略、动画和属性。这段代码的逻辑和作用是:

  • 调用 win.getDisplayContent() 方法,获取窗口所在的显示内容对象,并赋值给一个变量 displayContent。这个方法是 WindowState 类的一个公有方法,用于返回窗口所属的 DisplayContent 对象。DisplayContent 类是一个用于管理显示器和其上的窗口的类。
  • 调用 displayContent.getDisplayPolicy() 方法,获取显示内容的显示策略对象,并赋值给一个变量 displayPolicy。这个方法是 DisplayContent 类的一个公有方法,用于返回显示内容的 DisplayPolicy 对象。DisplayPolicy 类是一个用于处理显示器的状态、设置、旋转、装饰窗口等的类。
  • 获取窗口的窗口动画对象,并赋值给一个变量 winAnimator。这个对象是 WindowState 类的一个成员变量,用于存储窗口的 WindowStateAnimator 对象。WindowStateAnimator 类是一个用于管理窗口的动画和表面操作的类。
  • 判断 viewVisibility 是否不等于 View.GONE,如果是,则调用 win.setRequestedSize(requestedWidth, requestedHeight) 方法,根据给定的参数,设置窗口的请求宽度和高度。这个方法是 WindowState 类的一个公有方法,用于更新窗口的布局参数,并通知窗口的变化。
  • 定义三个整型变量 attrChangesflagChangesprivateFlagChanges,并初始化为 0。这些变量可能用于存储窗口的属性、标志和私有标志的变化情况,以便后续的处理。

总结: 这段代码是用于处理窗口的显示内容、显示策略、动画和属性的,它使用了公有方法、成员变量和整型变量。

if (attrs != null) {
                displayPolicy.adjustWindowParamsLw(win, attrs);
                attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
                attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), uid,
                        pid);
                int disableFlags =
                        (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
                if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
                    disableFlags = 0;
                }
                win.mDisableFlags = disableFlags;
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }
                if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) {
                    if (win.mAttrs.providedInsets == null || attrs.providedInsets == null
                            || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) {
                        throw new IllegalArgumentException(
                                "Insets amount can not be changed after the window is added.");
                    } else {
                        final int insetsTypes = attrs.providedInsets.length;
                        for (int i = 0; i < insetsTypes; i++) {
                            if (!win.mAttrs.providedInsets[i].idEquals(attrs.providedInsets[i])) {
                                throw new IllegalArgumentException(
                                        "Insets ID can not be changed after the window is added.");
                            }
                            final InsetsFrameProvider.InsetsSizeOverride[] overrides =
                                    win.mAttrs.providedInsets[i].getInsetsSizeOverrides();
                            final InsetsFrameProvider.InsetsSizeOverride[] newOverrides =
                                    attrs.providedInsets[i].getInsetsSizeOverrides();
                            if (!(overrides == null && newOverrides == null)) {
                                if (overrides == null || newOverrides == null
                                        || (overrides.length != newOverrides.length)) {
                                    throw new IllegalArgumentException(
                                            "Insets override types can not be changed after the "
                                                    + "window is added.");
                                } else {
                                    final int overrideTypes = overrides.length;
                                    for (int j = 0; j < overrideTypes; j++) {
                                        if (overrides[j].getWindowType()
                                                != newOverrides[j].getWindowType()) {
                                            throw new IllegalArgumentException(
                                                    "Insets override types can not be changed after"
                                                            + " the window is added.");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

这段代码,用于处理窗口的属性、标志、内边距和异常。这段代码的逻辑和作用是:

  • 判断 attrs 是否不为 null,如果是,则执行以下操作:
    • 调用 displayPolicy.adjustWindowParamsLw(win, attrs) 方法,根据给定的窗口和属性,调整窗口的参数。这个方法是 DisplayPolicy 类的一个公有方法,用于根据显示器的状态、设置、旋转等,修改窗口的布局参数。
    • 调用 sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid) 方法,根据给定的标志、窗口名、用户 ID 和进程 ID,返回一个合法的滑动标志。这个方法是 WindowManagerService 类的一个私有方法,用于检查窗口是否有滑动标志,如果有,判断是否有权限使用该标志,如果没有,移除该标志。
    • 调用 sanitizeSpyWindow(attrs.inputFeatures, win.getName(), uid, pid) 方法,根据给定的输入特性、窗口名、用户 ID 和进程 ID,返回一个合法的窥视窗口标志。这个方法是 WindowManagerService 类的一个私有方法,用于检查窗口是否有窥视窗口标志,如果有,判断是否有权限使用该标志,如果没有,移除该标志。
    • 定义一个整型变量 disableFlags,并将其赋值为 attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK,其中 attrs.systemUiVisibilityattrs.subtreeSystemUiVisibility 是窗口的系统 UI 可见性,DISABLE_MASK 是一个常量,表示需要禁用的系统 UI 标志。
    • 判断 disableFlags 是否不为 0,如果是,则判断 hasStatusBarPermission(pid, uid) 是否为 false,如果是,则将 disableFlags 赋值为 0。这里的 hasStatusBarPermission(pid, uid) 是一个方法,用于判断给定的进程 ID 和用户 ID 是否有状态栏的权限。
    • win.mDisableFlags 赋值为 disableFlags,其中 win.mDisableFlags 是窗口的禁用标志,用于存储窗口需要禁用的系统 UI 标志。
    • 判断 win.mAttrs.type 是否不等于 attrs.type,如果是,则抛出一个 IllegalArgumentException 异常,表示窗口的类型不能在窗口添加后改变。这里的 win.mAttrs.typeattrs.type 都是窗口的类型,用于标识窗口的种类。
    • 判断 win.mAttrs.providedInsetsattrs.providedInsets 是否不相等,如果是,则执行以下操作:
      • 判断 win.mAttrs.providedInsetsattrs.providedInsets 是否为 null,或者它们的长度是否不相等,如果是,则抛出一个 IllegalArgumentException 异常,表示窗口的内边距数量不能在窗口添加后改变。这里的 win.mAttrs.providedInsetsattrs.providedInsets 都是窗口的提供的内边距,用于存储窗口的内边距类型和大小。
      • 否则,定义一个整型变量 insetsTypes,并将其赋值为 attrs.providedInsets.length,表示窗口的内边距类型的数量。然后,使用一个 for 循环,从 0insetsTypes - 1,遍历每一个内边距类型,执行以下操作:
        • 判断 win.mAttrs.providedInsets[i].idEquals(attrs.providedInsets[i]) 是否为 false,如果是,则抛出一个 IllegalArgumentException 异常,表示窗口的内边距 ID 不能在窗口添加后改变。这里的 win.mAttrs.providedInsets[i]attrs.providedInsets[i] 都是窗口的第 i 个内边距类型,idEquals 是一个方法,用于判断两个内边距类型是否有相同的 ID。
        • 定义两个 InsetsFrameProvider.InsetsSizeOverride[] 类型的变量 overridesnewOverrides,并将它们分别赋值为 win.mAttrs.providedInsets[i].getInsetsSizeOverrides()attrs.providedInsets[i].getInsetsSizeOverrides(),表示窗口的第 i 个内边距类型的大小重写。这里的 getInsetsSizeOverrides 是一个方法,用于返回一个内边距类型的大小重写数组。
        • 判断 overridesnewOverrides 是否不相等,如果是,则执行以下操作:
          • 判断 overridesnewOverrides 是否为 null,或者它们的长度是否不相等,如果是,则抛出一个 IllegalArgumentException 异常,表示窗口的内边距重写类型不能在窗口添加后改变。
          • 否则,定义一个整型变量 overrideTypes,并将其赋值为 overrides.length,表示窗口的内边距重写类型的数量。然后,使用一个 for 循环,从 0overrideTypes - 1,遍历每一个内边距重写类型,执行以下操作:
            • 判断 overrides[j].getWindowType() 是否不等于 newOverrides[j].getWindowType(),如果是,则抛出一个 IllegalArgumentException 异常,表示窗口的内边距重写类型不能在窗口添加后改变。这里的 overrides[j]newOverrides[j] 都是窗口的第 j 个内边距重写类型,getWindowType 是一个方法,用于返回一个内边距重写类型的窗口类型。

总结: 这段代码是用于处理窗口的属性、标志、内边距和异常的,它使用了公有方法、私有方法、常量、变量、判断、运算和循环逻辑。

flagChanges = win.mAttrs.flags ^ attrs.flags;
                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                final boolean layoutChanged =
                        (attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0;
                if (layoutChanged || (attrChanges
                        & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
                    win.mLayoutNeeded = true;
                }
                if (layoutChanged && win.providesDisplayDecorInsets()) {
                    configChanged = displayPolicy.updateDecorInsetsInfo();
                }
                if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                        || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
                    win.mActivityRecord.checkKeyguardFlagsChanged();
                }

                if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                    updateNonSystemOverlayWindowsVisibilityIfNeeded(
                            win, win.mWinAnimator.getShown());
                }
                if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) {
                    winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
                            & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
                }
                // See if the DisplayWindowPolicyController wants to keep the activity on the window
                if (displayContent.mDwpcHelper.hasController()
                        && win.mActivityRecord != null && (!win.mRelayoutCalled || flagChanges != 0
                        || privateFlagChanges != 0)) {
                    int newOrChangedFlags = !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges;
                    int newOrChangedPrivateFlags =
                            !win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges;

                    if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
                            win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags,
                            win.mAttrs.flags,
                            win.mAttrs.privateFlags)) {
                        mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
                                win.mActivityRecord.getTask()));
                        Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
                                + " can't remain on display " + displayContent.getDisplayId());
                        return 0;
                    }
                }
            }

-计算 win.mAttrs.flagsattrs.flags 的按位异或运算,并赋值给一个变量 flagChanges,表示窗口的标志的变化情况。这里的 win.mAttrs.flagsattrs.flags 都是窗口的标志,用于控制窗口的显示和交互。

  • 计算 win.mAttrs.privateFlagsattrs.privateFlags 的按位异或运算,并赋值给一个变量 privateFlagChanges,表示窗口的私有标志的变化情况。这里的 win.mAttrs.privateFlagsattrs.privateFlags 都是窗口的私有标志,用于控制窗口的一些特殊功能。
  • 调用 win.mAttrs.copyFrom(attrs) 方法,根据给定的属性,复制窗口的属性,并赋值给一个变量 attrChanges,表示窗口的属性的变化情况。这个方法是 WindowManager.LayoutParams 类的一个公有方法,用于从另一个 WindowManager.LayoutParams 对象复制属性,并返回一个整型值,表示哪些属性发生了变化。
  • 定义一个布尔型变量 layoutChanged,并将其赋值为 attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED 的按位与运算是否不为 0,表示窗口的布局是否发生了变化。这里的 WindowManager.LayoutParams.LAYOUT_CHANGED 是一个常量,表示布局相关的属性发生了变化的标志位。
  • 判断 layoutChanged 是否为真,或者 attrChanges & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED 的按位与运算是否不为 0,如果是,则将 win.mLayoutNeeded 赋值为 true,表示窗口需要重新布局。这里的 WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED 是一个常量,表示系统 UI 可见性相关的属性发生了变化的标志位,win.mLayoutNeeded 是一个布尔型变量,用于标记窗口是否需要重新布局。
  • 判断 layoutChanged 是否为真,如果是,则判断 win.providesDisplayDecorInsets() 是否为真,如果是,则调用 displayPolicy.updateDecorInsetsInfo() 方法,并将其返回值赋值给一个变量 configChanged,表示显示策略的装饰内边距信息是否发生了变化。这里的 win.providesDisplayDecorInsets() 是一个方法,用于判断窗口是否提供了显示装饰内边距,displayPolicy.updateDecorInsetsInfo() 是一个方法,用于更新显示策略的装饰内边距信息,并返回一个布尔型值,表示是否发生了变化。
  • 判断 win.mActivityRecord 是否不为 null,如果是,则判断 flagChanges & FLAG_SHOW_WHEN_LOCKEDflagChanges & FLAG_DISMISS_KEYGUARD 的按位与运算是否不为 0,如果是,则调用 win.mActivityRecord.checkKeyguardFlagsChanged() 方法,检查窗口的锁屏相关的标志是否发生了变化。这里的 win.mActivityRecord 是一个对象,用于存储窗口所属的活动记录,FLAG_SHOW_WHEN_LOCKEDFLAG_DISMISS_KEYGUARD 都是常量,表示窗口在锁屏时是否显示或者解除锁屏的标志位,win.mActivityRecord.checkKeyguardFlagsChanged() 是一个方法,用于检查活动记录的锁屏相关的标志是否发生了变化,并做相应的处理。
  • 判断 privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS 的按位与运算是否不为 0,如果是,则调用 updateNonSystemOverlayWindowsVisibilityIfNeeded(win, win.mWinAnimator.getShown()) 方法,根据给定的窗口和显示状态,更新非系统覆盖窗口的可见性。这里的 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS 是一个常量,表示窗口是否隐藏非系统覆盖窗口的私有标志位,updateNonSystemOverlayWindowsVisibilityIfNeeded(win, win.mWinAnimator.getShown()) 是一个方法,用于根据窗口的私有标志位,控制非系统覆盖窗口的可见性。
  • 判断 attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED) 的按位与运算是否不为 0,如果是,则调用 winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0) 方法,根据给定的布尔型值,设置窗口动画是否忽略颜色空间。这里的 WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED 是一个常量,表示私有标志相关的属性发生了变化的标志位,winAnimator 是一个对象,用于存储窗口的窗口动画,winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0) 是一个方法,用于设置窗口动画是否忽略颜色空间,以便在不同的颜色空间下保持一致的外观,WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC 是一个常量,表示窗口是否忽略颜色空间的私有标志位³。
  • 判断 displayContent.mDwpcHelper.hasController() 是否为真,如果是,则判断 win.mActivityRecord 是否不为 null,如果是,则判断 !win.mRelayoutCalledflagChanges != 0privateFlagChanges != 0 是否为真,如果是,则执行以下操作:
    • 定义两个整型变量 newOrChangedFlagsnewOrChangedPrivateFlags,并将它们分别赋值为 !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges!win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges,表示窗口的新的或者改变的标志和私有标志。这里的 win.mRelayoutCalled 是一个布尔型变量,用于标记窗口是否已经重新布局过。
    • 判断 displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags, win.mAttrs.flags, win.mAttrs.privateFlags) 是否为 false,如果是,则执行以下操作:
      • 调用 mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY, win.mActivityRecord.getTask())) 方法,发送一个消息,将窗口所属的任务重新放置到默认的显示器上。这里的 mH 是一个对象,用于存储窗口管理服务的消息处理器,mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY, win.mActivityRecord.getTask()) 是一个方法,用于获取一个消息对象,其中 H.REPARENT_TASK_TO_DEFAULT_DISPLAY 是一个常量,表示重新放置任务到默认显示器的消息类型,win.mActivityRecord.getTask() 是一个方法,用于返回窗口所属的任务对象。
  • 调用 Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed, can't remain on display " + displayContent.getDisplayId()) 方法,打印一条警告日志,显示窗口所属的活动的标识符,窗口的标志发生了变化,不能保持在当前的显示器上。这里的 Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed, can't remain on display " + displayContent.getDisplayId()) 是一个方法,用于打印不同级别的日志,Slog.w 表示打印警告级别的日志,TAG_WM 是一个常量,表示窗口管理服务的日志标签,win.mActivityRecord 是一个对象,用于存储窗口所属的活动记录,displayContent.getDisplayId() 是一个方法,用于返回显示内容的显示器 ID 。
  • 返回 0,表示重新布局的结果为 0,即没有发生任何变化,结束方法。 这些语句是用于打印警告日志和返回重新布局的结果的,它们使用了方法、常量、对象和返回值。
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                    + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
            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;

            // 如果窗口即将变得可见,visibleOrAdding 可能会发生变化,这可能反过来
            // 更改输入法编辑器(IME)的目标。
            final boolean becameVisible =
                    (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                            && viewVisibility == View.VISIBLE;
            boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                    || becameVisible;
            boolean focusMayChange = win.mViewVisibility != viewVisibility
                    || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                    || (!win.mRelayoutCalled);

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

            final boolean wasVisible = win.isVisible();

            win.mRelayoutCalled = true;
            win.mInRelayout = true;

            win.setViewVisibility(viewVisibility);
            ProtoLog.i(WM_DEBUG_SCREEN_ON,
                    "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                            viewVisibility, new RuntimeException().fillInStackTrace());

            win.setDisplayLayoutNeeded();
            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

           // 只有在视图可见、是起始窗口或相关的 appToken 未隐藏时,我们才应该进行重新布局。
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());

            // 如果我们当前未运行退出动画,我们需要考虑是否开始一个。
            // 这必须在调用 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;
                // 当从窗口中移除 FLAG_SHOW_WALLPAPER 标志时,通常会在 DC#pendingLayoutChanges 中设置一个标志,并稍后更新壁纸目标。
// 但是,当窗口即将退出时,可能会立即从窗口中移除 FLAG_SHOW_WALLPAPER 标志,因此我们在这里立即更新壁纸目标。
// 否则,此窗口将停留在退出状态,其表面仍然保留在屏幕上。
// TODO(b/189856716):允许销毁即使属于 keyguard 目标的表面。
                if (wallpaperMayMove) {
                    displayContent.mWallpaperController.adjustWallpaperWindows();
                }
                tryStartExitingAnimation(win, winAnimator);
            }

这段代码是从WindowManagerService类中的relayoutWindow方法中摘取的,该方法用于处理应用请求改变其窗口的布局参数。代码的逻辑和作用如下:

  • 如果开启了布局调试模式(DEBUG_LAYOUT),那么打印窗口的重新布局信息,包括窗口的可见性,请求的宽度和高度,以及窗口的属性。
  • 如果窗口的透明度发生了变化(ALPHA_CHANGED),那么更新窗口动画对象的透明度(mAlpha)为窗口的属性中的透明度(attrs.alpha)。
  • 设置窗口的缩放比例为窗口的请求宽度和高度(mRequestedWidth 和 mRequestedHeight)。
  • 如果窗口的表面内边距(surfaceInsets)不为零,那么设置窗口动画对象的不透明状态为假(setOpaqueLocked(false))。
  • 获取窗口的旧的可见性(oldVisibility)。
  • 判断窗口是否从不可见变为可见(becameVisible),如果是,那么可能会影响输入法编辑器(IME)的目标(imMayMove)。
  • 判断窗口的可见性是否发生了变化(focusMayChange),如果是,那么可能会影响窗口的焦点状态。
  • 判断窗口是否有壁纸(wallpaperMayMove),如果是,那么可能会影响壁纸的移动。
  • 如果窗口的安全标志(FLAG_SECURE)发生了变化,并且窗口动画对象有表面控制器(mSurfaceController),那么设置表面控制器的安全状态为窗口的安全状态(isSecureLocked())。
  • 获取窗口的当前可见性(wasVisible)。
  • 设置窗口的重新布局标志(mRelayoutCalled)和重新布局状态(mInRelayout)为真。
  • 设置窗口的视图可见性(setViewVisibility(viewVisibility))。
  • 打印窗口的重新布局日志(ProtoLog.i(WM_DEBUG_SCREEN_ON, …))。
  • 设置窗口需要显示布局(setDisplayLayoutNeeded())。
  • 设置窗口的内边距待定标志(mGivenInsetsPending)为重新布局的内边距待定标志(RELAYOUT_INSETS_PENDING)。
  • 判断是否应该进行重新布局(shouldRelayout),只有当视图可见,或者是起始窗口,或者相关的活动记录可见时,才应该进行重新布局。
  • 如果不应该进行重新布局,并且窗口动画对象有表面,并且窗口没有运行退出动画(mAnimatingExit),那么:
    • 如果开启了可见性调试模式(DEBUG_VISIBILITY),那么打印窗口的重新布局不可见信息(Slog.i(TAG_WM, …))。
    • 设置结果标志位(result)为表面发生了变化(RELAYOUT_RES_SURFACE_CHANGED)。
    • 如果窗口的壁纸标志(FLAG_SHOW_WALLPAPER)发生了变化,那么调整壁纸窗口(adjustWallpaperWindows())。
    • 尝试开始退出动画(tryStartExitingAnimation(win, winAnimator))。
      总结: 这段代码是用于处理应用窗口的重布局请求,更新窗口的状态和布局,以及执行相应的动画和调整。
// Create surfaceControl before surface placement otherwise layout will be skipped
            // (because WS.isGoneForLayout() is true when there is no surface.
            if (shouldRelayout && outSurfaceControl != null) {
                try {
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);

                    ProtoLog.w(WM_ERROR,
                            "Exception thrown when creating surface for client %s (%s). %s",
                            client, win.mAttrs.getTitle(), e);
                    Binder.restoreCallingIdentity(origId);
                    return 0;
                }
            }

这段代码的逻辑和作用是:

  • 首先,检查是否需要重新布局窗口(shouldRelayout)以及是否有输出表面控制器(outSurfaceControl)的引用。如果两者都为真,那么执行下一步,否则跳过这段代码。
  • 然后,尝试调用 createSurfaceControl 方法,该方法会创建一个新的 SurfaceControl 对象,用于管理窗口的表面。这个方法需要传入输出表面控制器(outSurfaceControl),结果标志位(result),窗口对象(win)和窗口动画对象(winAnimator)作为参数。如果成功,那么返回结果标志位,否则执行异常处理。
  • 异常处理的逻辑是:更新输入窗口的状态,打印错误日志,恢复调用者的身份,返回 0 作为结果标志位。
    这段代码的作用是在重新布局窗口之前创建表面控制器,因为如果没有表面控制器,那么窗口的布局会被跳过(因为 WS.isGoneForLayout() 会返回真)。表面控制器是用于与 SurfaceFlinger 通信的对象,它可以控制窗口的表面的位置,大小,透明度,变换等属性。表面控制器是通过事务包(Transaction)来更新的,这样可以保证多个表面控制器的状态同时生效。
         // 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 */);
            if (shouldRelayout) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");

                result = win.relayoutVisibleWindow(result);

                if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    focusMayChange = true;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.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 (outSurfaceControl != null) {
                    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.getSurfaceControl(outSurfaceControl);
                        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());
                            outSurfaceControl.release();
                        } finally {
                            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                        }
                    }
                }

                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }

            if (focusMayChange) {
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }

这段代码的逻辑大致如下:

  • 首先,如果当前正在推迟布局过程,但是客户端(应用程序)对新的输出值感兴趣,那么就需要强制执行一次布局。这是通过调用 mWindowPlacerLocked.performSurfacePlacement(true) 方法来实现的,它会遍历所有的窗口并更新它们的位置和大小。
  • 然后,根据 shouldRelayout 变量的值,分为两种情况处理。shouldRelayout 变量表示是否需要重新布局窗口,它是根据输入参数和窗口的当前状态计算得到的。
    • 如果 shouldRelayout 为真,那么执行以下步骤:
      • 使用 Trace.traceBegin 和 Trace.traceEnd 方法在日志中记录开始和结束的时间戳,方便调试和性能分析。这里的 TRACE_TAG_WINDOW_MANAGER 是一个常量,表示日志的标签。“relayoutWindow: viewVisibility_1” 是一个字符串,表示日志的消息。
      • 调用 win.relayoutVisibleWindow(result) 方法,根据窗口的可见性,重新布局和调整窗口的大小,并更新 result 变量的值。这个方法会根据窗口的类型、状态、动画等因素,创建或销毁窗口的表面控制,设置窗口的 Z 轴顺序,更新窗口的布局参数等。
      • 如果 result 变量的值与 WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME 常量进行按位与运算的结果不为零,那么表示这是窗口第一次布局,可能会导致焦点的变化,所以将 focusMayChange 变量设为真。WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME 常量表示一个标志位,用于指示窗口的布局结果。
      • 如果 win.mAttrs.type 等于 TYPE_INPUT_METHOD 常量,表示这个窗口是一个输入法窗口,并且 displayContent.mInputMethodWindow 为 null,表示当前没有输入法窗口,那么就调用 displayContent.setInputMethodWindowLocked(win) 方法,将这个窗口设置为输入法窗口,并更新输入法的可见性和位置⁴。然后将 imMayMove 变量设为真,表示输入法可能会移动。
      • 调用 win.adjustStartingWindowFlags() 方法,根据窗口的类型和状态,调整窗口的标志位,例如是否需要显示启动窗口,是否需要隐藏导航栏等。
    • 如果 shouldRelayout 为假,那么执行以下步骤:
      • 同样使用 Trace.traceBegin 和 Trace.traceEnd 方法记录日志,这里的消息是 “relayoutWindow: viewVisibility_2”。
      • 将 winAnimator.mEnterAnimationPending 和 winAnimator.mEnteringAnimation 两个变量设为假,表示窗口不需要执行进入动画。
      • 如果 outSurfaceControl 变量不为 null,表示有一个 SurfaceControl 对象作为输出参数,那么根据以下条件处理:
        • 如果 viewVisibility 等于 View.VISIBLE 常量,表示窗口是可见的,并且 winAnimator.hasSurface() 方法返回真,表示窗口有一个有效的表面控制,那么就调用 winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl) 方法,将窗口的表面控制复制到输出参数中,让客户端可以使用它⁴。这种情况可能发生在客户端已经被告知要隐藏窗口,但是消息还没有处理,或者客户端想要绘制最后一帧的时候。
        • 否则,如果 DEBUG_VISIBILITY 常量为真,表示需要打印调试信息,那么就使用 Slog.i 方法在日志中记录一条信息,表示正在释放窗口的表面控制。然后使用 try-finally 语句,在 finally 块中调用 Trace.traceEnd 方法结束日志记录,保证无论是否发生异常,都能正确地结束日志记录。在 try 块中,调用 outSurfaceControl.release() 方法,释放输出参数的表面控制,表示不再需要它。
  • 最后,如果 focusMayChange 变量为真,表示可能需要更新焦点窗口,那么就调用 updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true) 方法,根据窗口的可见性和 Z 轴顺序,更新当前的焦点窗口,并返回一个布尔值,表示是否发生了焦点变化。如果返回真,那么就将 imMayMove 变量设为假,表示输入法不需要移动。

总之,这段代码的作用是根据客户端的请求,重新布局和调整一个窗口的大小、位置、可见性和表面控制,并根据窗口的类型和状态,更新输入法和焦点窗口的情况。

// 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;
            if (imMayMove) {
                displayContent.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.
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                }
            }

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

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

            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
            configChanged |= displayContent.updateOrientation();
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

            if (toBeDisplayed && win.mIsWallpaper) {
                displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
            }
            if (win.mActivityRecord != null) {
                win.mActivityRecord.updateReportedVisibilityLocked();
            }
            if (displayPolicy.areSystemBarsForcedConsumedLw()) {
                result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
            }
            if (!win.isGoneForLayout()) {
                win.mResizedWhileGone = false;
            }

            if (outFrames != null && outMergedConfiguration != null) {
                win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
                        false /* useLatestConfig */, shouldRelayout);

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

            if (outInsetsState != null) {
                outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
            }

这段代码的逻辑大致如下:

  • 这段代码是在之前的代码的基础上,继续处理窗口的布局结果和状态,以及更新窗口的相关组件和配置。
  • 首先,根据 result 变量的值与 WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME 常量进行按位与运算的结果,判断是否是窗口第一次布局,如果是,就将 toBeDisplayed 变量设为真。WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME 常量表示一个标志位,用于指示窗口的布局结果。
  • 然后,如果 imMayMove 变量为真,表示输入法可能会移动,那么执行以下步骤:
    • 调用 displayContent.computeImeTarget(true) 方法,计算当前的输入法目标窗口,即需要显示输入法的窗口。
    • 如果 toBeDisplayed 变量为真,表示窗口是第一次布局,那么就调用 displayContent.assignWindowLayers(false) 方法,重新分配窗口的 Z 轴顺序,即窗口的堆叠顺序。这里有一个小技巧,即即使输入法没有在窗口列表中移动,但是如果它之前是隐藏的,那么它的 Z 轴顺序可能是过时的,所以需要重新计算。
  • 接着,如果 wallpaperMayMove 变量为真,表示壁纸可能会移动,那么就将 displayContent.pendingLayoutChanges 变量的值与 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER 常量进行按位或运算,表示需要重新布局壁纸。WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER 常量表示一个标志位,用于指示布局的变化类型。
  • 然后,如果 win.mActivityRecord 变量不为 null,表示这个窗口对应一个活动,那么就调用 displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord) 方法,通知未知应用可见性控制器,这个活动的窗口已经重新布局了。
  • 接下来,使用 Trace.traceBegin 和 Trace.traceEnd 方法在日志中记录开始和结束的时间戳,方便调试和性能分析。这里的 TRACE_TAG_WINDOW_MANAGER 是一个常量,表示日志的标签。“relayoutWindow: updateOrientation” 是一个字符串,表示日志的消息。
  • 然后,调用 configChanged |= displayContent.updateOrientation() 方法,根据窗口的布局结果,更新屏幕的方向,并将返回值与 configChanged 变量进行按位或运算,表示是否发生了配置的变化。
  • 接着,如果 toBeDisplayed 变量为真,表示窗口是第一次布局,并且 win.mIsWallpaper 变量为真,表示这个窗口是一个壁纸窗口,那么就调用 displayContent.mWallpaperController.updateWallpaperOffset(win, false) 方法,更新壁纸窗口的偏移量。
  • 然后,如果 win.mActivityRecord 变量不为 null,表示这个窗口对应一个活动,那么就调用 win.mActivityRecord.updateReportedVisibilityLocked() 方法,更新活动的可见性报告,即向应用程序报告活动的可见性和透明度。
  • 接下来,如果 displayPolicy.areSystemBarsForcedConsumedLw() 方法返回真,表示系统栏被强制消耗,即窗口不能延伸到系统栏的区域,那么就将 result 变量的值与 WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS 常量进行按位或运算,表示窗口的布局结果。WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS 常量表示一个标志位,用于指示窗口的布局结果。
  • 然后,如果 win.isGoneForLayout() 方法返回假,表示窗口没有消失,那么就将 win.mResizedWhileGone 变量设为假,表示窗口没有在消失的时候被调整大小。
  • 接着,如果 outFrames 变量和 outMergedConfiguration 变量都不为 null,表示有一个 Rect 对象和一个 MergedConfiguration 对象作为输出参数,那么就调用 win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration, false, shouldRelayout) 方法,根据窗口的布局结果,填充窗口的客户端区域的边框和配置。然后调用 win.onResizeHandled() 方法,设置窗口的调整大小处理标志,表示窗口已经处理了调整大小的请求。
  • 最后,如果 outInsetsState 变量不为 null,表示有一个 InsetsState 对象作为输出参数,那么就调用 outInsetsState.set(win.getCompatInsetsState(), true) 方法,根据窗口的兼容插入状态,设置输出参数的插入状态,即窗口的边缘和内部区域的信息。

总之,这段代码的作用是根据窗口的布局结果和状态,更新窗口的相关组件和配置,例如输入法、壁纸、屏幕方向、可见性报告、系统栏、边框等,并将一些输出值返回给客户端。

ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                    win, focusMayChange);

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

            final boolean winVisibleChanged = win.isVisible() != wasVisible;
            if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
                dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type,
                        win.isVisible(), false /* removed */);
            }
            // Notify listeners about IME input target window visibility change.
            final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
            if (isImeInputTarget && winVisibleChanged) {
                dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
                        win.isVisible() /* visible */, false /* removed */);
            }

            if (outSyncIdBundle != null) {
                final int maybeSyncSeqId;
                if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
                        && win.mSyncSeqId > lastSyncSeqId) {
                    maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
                    win.markRedrawForSyncReported();
                } else {
                    maybeSyncSeqId = -1;
                }
                outSyncIdBundle.putInt("seqid", maybeSyncSeqId);
            }

            if (configChanged) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                        "relayoutWindow: postNewConfigurationToHandler");
                displayContent.sendNewConfiguration();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
            if (outActiveControls != null) {
                getInsetsSourceControls(win, outActiveControls);
            }
        }

        Binder.restoreCallingIdentity(origId);
        return result;
    }

这段代码的逻辑大致如下:

  • 这段代码是在之前的代码的基础上,继续处理窗口的布局结果和状态,以及返回一些输出值给客户端。
  • 首先,使用 ProtoLog.v 方法在日志中记录一条信息,表示正在重新布局一个窗口,并显示窗口的名字和是否可能导致焦点变化的布尔值。ProtoLog 是一个用于打印协议缓冲区日志的工具类。WM_DEBUG_FOCUS 是一个常量,表示日志的标签。“Relayout of %s: focusMayChange=%b” 是一个字符串,表示日志的消息,其中 %s 和 %b 是占位符,分别表示窗口的名字和布尔值。
  • 然后,如果 DEBUG_LAYOUT 常量为真,表示需要打印调试信息,那么就使用 Slog.v 方法在日志中记录一条信息,表示重新布局完成,并显示窗口的名字和输出的边框。Slog 是一个用于打印系统日志的工具类。TAG_WM 是一个常量,表示日志的标签。"Relayout complete " + win + “: outFrames=” + outFrames 是一个字符串,表示日志的消息,其中 win 和 outFrames 是变量,分别表示窗口和输出的边框。
  • 接着,将 win.mInRelayout 变量设为假,表示窗口不再处于重新布局的状态。
  • 然后,根据 win.isVisible() 方法和 wasVisible 变量的值,判断窗口的可见性是否发生了变化,如果是,就将 winVisibleChanged 变量设为真。win.isVisible() 方法返回一个布尔值,表示窗口是否可见。wasVisible 变量是一个布尔值,表示窗口的之前的可见性。
  • 接下来,如果 win.isImeOverlayLayeringTarget() 方法和 winVisibleChanged 变量都为真,表示这个窗口是一个输入法覆盖层的目标,并且它的可见性发生了变化,那么就调用 dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, win.isVisible(), false) 方法,向客户端发送一个广播,通知输入法目标覆盖层的可见性变化。win.isImeOverlayLayeringTarget() 方法返回一个布尔值,表示窗口是否是一个输入法覆盖层的目标,即是否需要在输入法上方显示一个半透明的遮罩层。client 是一个 IWindow 对象,表示客户端的窗口接口。client.asBinder() 方法返回一个 IBinder 对象,表示客户端的窗口代理。win.mAttrs 是一个 WindowManager.LayoutParams 对象,表示窗口的属性和状态。win.mAttrs.type 是一个整数,表示窗口的类型。win.isVisible() 方法返回一个布尔值,表示窗口是否可见。false 是一个布尔值,表示窗口是否被移除。
  • 然后,根据 win.getDisplayContent().getImeInputTarget() 方法和 win 变量的值,判断这个窗口是否是输入法的输入目标,即是否需要显示输入法的窗口。如果是,就将 isImeInputTarget 变量设为真。win.getDisplayContent() 方法返回一个 DisplayContent 对象,表示窗口所在的显示内容。win.getDisplayContent().getImeInputTarget() 方法返回一个 WindowState 对象,表示当前的输入法输入目标。win 是一个 WindowState 对象,表示一个窗口的状态。
  • 接着,如果 isImeInputTarget 变量和 winVisibleChanged 变量都为真,表示这个窗口是输入法的输入目标,并且它的可见性发生了变化,那么就调用 dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(), win.isVisible(), false) 方法,向客户端发送一个广播,通知输入法输入目标的可见性变化。win.mClient 是一个 IWindow 对象,表示客户端的窗口接口。win.mClient.asBinder() 方法返回一个 IBinder 对象,表示客户端的窗口代理。win.isVisible() 方法返回一个布尔值,表示窗口是否可见。false 是一个布尔值,表示窗口是否被移除。
  • 然后,如果 outSyncIdBundle 变量不为 null,表示有一个 Bundle 对象作为输出参数,那么执行以下步骤:
    • 声明一个整数变量 maybeSyncSeqId,用于存储可能的同步序列号。
    • 如果 USE_BLAST_SYNC 常量为真,表示使用 BLAST 同步机制,并且 win.useBLASTSync() 方法返回真,表示窗口使用 BLAST 同步机制,并且 viewVisibility 等于 View.VISIBLE 常量,表示窗口是可见的,并且 win.mSyncSeqId 变量大于 lastSyncSeqId 变量,表示窗口的同步序列号更新了,那么执行以下步骤:
      • 如果 win.shouldSyncWithBuffers() 方法返回真,表示窗口需要与缓冲区同步,那么就将 maybeSyncSeqId 变量的值设为 win.mSyncSeqId 变量的值,即窗口的同步序列号。否则,就将 maybeSyncSeqId 变量的值设为 -1,表示不需要同步。
      • 调用 win.markRedrawForSyncReported() 方法,标记窗口已经报告了同步。
    • 否则,就将 maybeSyncSeqId 变量的值设为 -1,表示不需要同步。
    • 调用 outSyncIdBundle.putInt(“seqid”, maybeSyncSeqId) 方法,将 maybeSyncSeqId 变量的值作为一个整数,存储到输出参数的 Bundle 对象中,使用 “seqid” 作为键。
  • 接下来,如果 configChanged 变量为真,表示发生了配置的变化,那么执行以下步骤:
    • 使用 Trace.traceBegin 和 Trace.traceEnd 方法在日志中记录开始和结束的时间戳,方便调试和性能分析。这里的 TRACE_TAG_WINDOW_MANAGER 是一个常量,表示日志的标签。“relayoutWindow: postNewConfigurationToHandler” 是一个字符串,表示日志的消息。
    • 调用 displayContent.sendNewConfiguration() 方法,向窗口管理器的处理器发送一个消息,表示需要更新配置。
  • 然后,如果 outActiveControls 变量不为 null,表示有一个 InsetsSourceControl[] 数组作为输出参数,那么就调用 getInsetsSourceControls(win, outActiveControls) 方法,根据窗口的插入源,获取插入源控制,并存储到输出参数的数组中。插入源是一种用于描述窗口的边缘和内部区域的信息的对象,例如状态栏、导航栏、输入法等。插入源控制是一种用于控制插入源的可见性和行为的对象,例如隐藏或显示状态栏等。
  • 然后,调用 Binder.restoreCallingIdentity(origId) 方法,恢复之前保存的调用者的身份,即调用 relayoutWindow 方法的应用程序的身份。这是为了保证安全性和权限的检查,避免跨进程的调用造成混乱。origId 是一个长整数,表示之前保存的调用者的身份。
  • 最后,返回 result 变量的值,表示窗口的布局结果,它是一个整数,包含了一些标志位,用于指示窗口的大小、位置、可见性、表面控制、系统栏等信息。

最后来一个简单的总结:

relayoutWindow 是 Android 窗口管理器的一个方法,它的作用是根据应用程序的请求,重新布局和调整一个窗口的大小、位置、可见性和表面控制,并返回一些输出值给客户端。它的逻辑大致如下:

  • 首先,它会根据输入参数和窗口的当前状态,判断是否需要重新布局窗口,是否需要强制执行一次布局,是否可能导致焦点、输入法或壁纸的变化等。
  • 然后,它会根据窗口的可见性,重新布局和调整窗口的大小,并更新窗口的表面控制,设置窗口的 Z 轴顺序,更新窗口的布局参数等。
  • 接着,它会根据窗口的布局结果,更新窗口的相关组件和配置,例如输入法、壁纸、屏幕方向、可见性报告、系统栏、边框等,并将一些输出值返回给客户端,例如窗口的边框、配置、插入状态、插入源控制等。
  • 最后,它会恢复之前保存的调用者的身份,保证安全性和权限的检查,避免跨进程的调用造成混乱。
  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值