Android WindowManagerService--06:Window的relayout过程

本文转载自:Android R WindowManagerService模块(3) Window的relayout过程

本文基于Android 11.0源码分析

前言

  在Window的添加过程中,主要添加窗口时的WindowManagerService#addWindow()方法进行了主要总结,通过WindowManagerService#addWindow()方法,system_server中创建了一个对应的窗口管理对象,并且放置到了适合的容器中。这篇文章中将对之后窗口的relayout过程进行总结。

1.requestLayout过程

  回到添加窗口流程中的ViewRootImpl#setView()方法中,在调用mWindowSession#addToDisplayAsUser()方法之前,先是调用了requestLayout()方法,并最终通过Chreographer对象设置了一个VSync监听:

//  frameworks/base/core/java/android/view/ViewRootImpl.java
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 设置一个同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 设置VSync监听
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

当收到VSync信号后,mTraversalRunnable线程的run方法:

//frameworks/base/core/java/android/view/ViewRootImpl.java
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

执行doTraversal()方法进行遍历操作:

//frameworks/base/core/java/android/view/ViewRootImpl.java
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

在performTraversals方法中执行relayoutWindow()方法,通过IWindowSession接口实例向WMS发起布局请求:

//frameworks/base/core/java/android/view/ViewRootImpl.java
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize, mBlastSurfaceControl);
        ......
        return relayoutResult;
    }

这里Session.java的relayout()方法,之后将进入WMS#relayoutWindow()方法中,在这个方法中进行布局工作。下面详细来看这个方法。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    /**
     * @param session                IWindowSession对象,用于ViewRootImpl向WMS发起交互
     * @param client                 IWindow对象,代表客户端的Window,用于WMS向ViewRootImpl发起交互
     * @param seq                    请求序列
     * @param attrs                  窗口各种参数和属性
     * @param requestedWidth         客户端请求窗口的宽
     * @param requestedHeight        客户端请求窗口的高
     * @param viewVisibility         View的可见性
     * @param flags                  标记
     * @param frameNumber
     * @param outFrame               返回给ViewRootImpl的窗口框架
     * @param outContentInsets                
     * @param outVisibleInsets
     * @param outStableInsets
     * @param outBackdropFrame
     * @param outCutout
     * @param mergedConfiguration
     * @param outSurfaceControl      返回给ViewRootImpl的Surface管理对象
     * @param outInsetsState
     * @param outActiveControls
     * @param outSurfaceSize
     * @param outBLASTSurfaceControl
     * @return
     */
    public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {
                ......
        boolean configChanged;
        synchronized (mGlobalLock) {
            // 获取WindowState、DisplayContent、DisplayPolicy、WindowStateAnimator对象
            final WindowState win = windowForClientLocked(session, client, false);
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            WindowStateAnimator winAnimator = win.mWinAnimator;
            // 给WindowState设置来自应用请求的窗口大小
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight);
            }
            // 设置Frame number
            win.setFrameNumber(frameNumber);

            final DisplayContent dc = win.getDisplayContent();
            // 如果此时没有执行Configuration的更新,试图结束衔接动画
            if (!dc.mWaitingForConfig) {
                win.finishSeamlessRotation(false /* timeout */);
            }
            // 用来标记属性是否发生变化
            int attrChanges = 0;
            int flagChanges = 0;
            int privateFlagChanges = 0;
            int privflagChanges = 0;
            if (attrs != null) {
                // 调整特殊类型的Window#attrs属性
                displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                // 针对壁纸窗口调整Window#attrs属性
                win.mToken.adjustWindowParams(win, attrs);
                // 调整mSystemUiVisibility属性,控制status bar的显示
                if (seq == win.mSeq) {
                    int systemUiVisibility = attrs.systemUiVisibility
                            | attrs.subtreeSystemUiVisibility;
                                                ......
                    }
                    win.mSystemUiVisibility = systemUiVisibility;
                }

                // PRIVATE_FLAG_PRESERVE_GEOMETRY将忽略新x、y、width、height值,使用旧值
                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;
                }
                // 确定flag是否发生变化
                flagChanges = win.mAttrs.flags ^ attrs.flags;
                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                // 根据flag是否发生变化做出对应响应,略....
                .......
            }

            // 根据应用请求设置宽高,获取窗口缩放比例
            win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
            // 窗口此时的可见状态
            final int oldVisibility = win.mViewVisibility;
            // 窗口是否要由不可见状态转变为可见状态
            final boolean becameVisible =
                    (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                            && viewVisibility == View.VISIBLE;
            // 是否需要移除IME窗口
            boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                    || becameVisible;
            // 确定是否需要更新focus状态,第一次执行relayout时,mRelayoutCalled为false
            boolean focusMayChange = win.mViewVisibility != viewVisibility
                    || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                    || (!win.mRelayoutCalled);
            // 如果窗口可见性发生变化,且该窗口允许显示在壁纸之上,则对壁纸窗口进行处理
            boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                    && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
            wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;

            win.mRelayoutCalled = true; // 设置WindowState#mRelayoutCalled为true
            win.mInRelayout = true; // 设置WindowState#mInRelayout为true,表示在relayout过程中,relayout完毕后,重置为false
            // 更新窗口的可见性
            win.setViewVisibility(viewVisibility);
            // 通知DisplayContent,需要进行重新布局
            win.setDisplayLayoutNeeded();
            // 表示是否在等待设置Inset
            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

            // 如果窗口可见,进行重新布局
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
            // 执行一遍刷新操作
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);

            // 是否需要进行relayout操作
            if (shouldRelayout) {
                // 布局
                result = win.relayoutVisibleWindow(result, attrChanges);
                try {
                    // 创建Surface
                    result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
                            result, win, winAnimator);
                } catch (Exception e) {
                    return 0;
                }
                // 如果是第一次relayout操作,需要focusMayChange
                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;
                }
            } else {
                                ......
            }
            // 更新FocusWindow
            if (focusMayChange) {
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }
            // 是否首次更新
            boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
            // 更新屏幕显示方向
            configChanged = displayContent.updateOrientation();
            // 对壁纸窗口的特殊处理,更新偏移量
            if (toBeDisplayed && win.mIsWallpaper) {
                displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
            }
            if (win.mActivityRecord != null) {
                win.mActivityRecord.updateReportedVisibilityLocked();
            }
            ......
            // 更新mergedConfiguration对象
            if (shouldRelayout) {
                win.getMergedConfiguration(mergedConfiguration);
            } else {
                win.getLastReportedMergedConfiguration(mergedConfiguration);
            }
            // 设置各种Inset和DisplayCutout
            win.getCompatFrame(outFrame);
            win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
                    outStableInsets);
            outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
            outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
            outInsetsState.set(win.getInsetsState(), win.isClientLocal());

            // 更新relayout标记,表示可接受touch事件
            result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;

            win.mInRelayout = false;                // 重置为false,表示relayout过程完成
            // configuration发生变化时,更新全局Configuration
            if (configChanged) {
                displayContent.sendNewConfiguration();
            }
            // 设置outSurfaceSize
            if (winAnimator.mSurfaceController != null) {
                outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
                                         winAnimator.mSurfaceController.getHeight());
            }
            getInsetsSourceControls(win, outActiveControls);
        }

        return result;
    }

在进入这个方法后,首先通过IWindow对象获取了对应的WindowState对象,之后将以WindowState为主体进行各种操作。这个方法很长,但主要有以下几个步骤:

  1. WindowState#setRequestedSize()设置WindowState宽高;

  2. DisplayPolicy#adjustWindowParamsLw()调整窗口属性;

  3. WindowState#setWindowScale()设置缩放比例;

  4. WindowState#setViewVisibility()更新可见性属性;

  5. WindowState#relayoutVisibleWindow()处理FLAG_TURN_SCREEN_ON标记;

  6. WMS#createSurfaceControl()创建Surface;

  7. WMS#updateFocusedWindowLocked()更新焦点窗口;

  8. 设置MergedConfiguration对象返回客户端;

  9. 设置windowFrame和Inset返回客户端。

2.relayoutWindow逐步分析

  上一小节分析requestLayout()过程中,最终会调用到WMS的relayoutWindow()方法,该方法中有很多的处理过程,下面对这些步骤进行分析总结。

2.1 WindowState#setRequestedSize()设置WindowState宽高

  通过WindowState#setRequestedSize()方法,设置WindowState对象mRequestedWidth和mRequestedHeight属性,这两属性表示来自应用请求设置的宽高:

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void setRequestedSize(int requestedWidth, int requestedHeight) {
        if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
            mLayoutNeeded = true;
            mRequestedWidth = requestedWidth;
            mRequestedHeight = requestedHeight;
        }
    }

2.2 DisplayPolicy#adjustWindowParamsLw()调整窗口属性

  接下来,会根据系统当前条件,对客户端传入的布局参数进行调整,主要通过DisplayPolicy#adjustWindowParamsLw(win, attrs, pid, uid)调整,对于壁纸这类特殊窗口,会通过WallpaperWindowToken#adjustWindowParams()方法进行调整。如果调整后的布局参数发生了变化,则进行相关状态的处理。这部分代码略过。

2.3 WindowState#setWindowScale()设置缩放比例

  通过WindowState#setWindowScale()方法,设置WindowState对象mHScale和mVScale属性,这两属性表示窗口在横竖方向的缩放比例:

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void setWindowScale(int requestedWidth, int requestedHeight) {
        final boolean scaledWindow = (mAttrs.flags & FLAG_SCALED) != 0;
        if (scaledWindow) {
            mHScale = (mAttrs.width  != requestedWidth)  ?
                    (mAttrs.width  / (fl性oat)requestedWidth) : 1.0f;
            mVScale = (mAttrs.height != requestedHeight) ?
                    (mAttrs.height / (float)requestedHeight) : 1.0f;
        } else {
            mHScale = mVScale = 1;
        }
    }

可以看到,只有对在布局参数中设置了FLAG_SCALED标记的窗口,才会计算缩放比例,否则都是1。

2.4 WindowState#setViewVisibility()更新可见性

  通过WindowState#setViewVisibility()方法,设置WindowState对象mViewVisibility属性,该属性表示该Window的可见性:

//  frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void setViewVisibility(int viewVisibility) {
        mViewVisibility = viewVisibility;
                ......
    }

mViewVisibility有三个值,均来自于APP端的View:

  • View.VISIBLE:表示该窗口可见;

  • View.INVISIBLE:表示该窗口不可见,但仍然占据空间,其Surface依然存在;

  • View.GONE:表示该窗口不可见,并且不再占据空间,其Surface也被销毁。

2.5 WindowState#relayoutVisibleWindow()处理FLAG_TURN_SCREEN_ON

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    int relayoutVisibleWindow(int result, int attrChanges) {
        // 是否可见
        final boolean wasVisible = isVisibleLw();
        // 如果不可见,表示第一次进行relayout
        result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
        // 是否执行退出动画
        if (mAnimatingExit) {
            ....
        }
        // 是否在销毁Surface列表中,等待被Destory
        if (mDestroying) {
            mDestroying = false;
            mWmService.mDestroySurface.remove(this);
        }

        if (!wasVisible) {
            // 表示即将执行进入动画
            mWinAnimator.mEnterAnimationPending = true;
        }
        // 最近一次可见时的屏幕方向
        mLastVisibleLayoutRotation = getDisplayContent().getRotation();
        // 表示开始执行进入动画
        mWinAnimator.mEnteringAnimation = true;
        try {
            prepareWindowToDisplayDuringRelayout(wasVisible);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        // 当布局参数中像素格式发生变化后的处理
        if ((attrChanges & FORMAT_CHANGED) != 0) {
            .....
        }
        // 当拖拽发生resize时
        if (isDragResizeChanged()) {
                        ......
        }
        ......
        return result;
    }

在这个方法中,调用prepareWindowToDisplayDuringRelayout()方法,针对亮屏flag进行处理:

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
        // 是否带有点亮屏幕的标记
        final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
                || (mActivityRecord != null && mActivityRecord.canTurnScreenOn());
        if (hasTurnScreenOnFlag) {
                ......
            if (allowTheaterMode && canTurnScreenOn
                        && (mWmService.mAtmInternal.isDreaming()
                        || !mPowerManagerWrapper.isInteractive())) {
                // 触发亮屏
                mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
                        PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
            }

            if (mActivityRecord != null) {
                mActivityRecord.setCurrentLaunchCanTurnScreenOn(false);
            }
        }

        if (wasVisible) {
            return;
        }
                ......
    }

以上方法中,如果布局参数或对应的ActivityRecord中携带有亮屏相关flag,则进行亮屏。窗口中通过FLAG_TURN_SCREEN_ON进行亮屏,就是在这里触发的。

2.6 WMS#createSurfaceControl()创建Surface

  在客户端ViewRootImpl中,有一个Surface对象,它是这个ViewRootImpl中View显示内容的最终呈现者。但它在创建时,仅仅是一个空壳,没有任何内容。在relayout()时,ViewRootImpl将SurfaceControl对象传递给了WMS,WMS中会创建一个SurfaceControl对象,并将它复制给传入的SurfaceControl,最终ViewRootImpl从SurfaceControl中获取内容:

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
    private int createSurfaceControl(SurfaceControl outSurfaceControl,
            SurfaceControl outBLASTSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
        // 此时该WindowState还没有对应Surface
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        WindowSurfaceController surfaceController;
        try {
            // 创建一个WindowSurfaceController对象
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally {
        }
        if (surfaceController != null) {
            // 将WindowSurfaceController中的mSurfaceControl复制给outSurfaceControl
            surfaceController.getSurfaceControl(outSurfaceControl);
            surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
        } 

        return result;
    }

以上方法中:

  • 首先给result变量设置RELAYOUT_RES_SURFACE_CHANGED标记,表示relayout过程的第二个阶段;

  • 然后通过WindowStateAnimator#createSurfaceLocked()创建了WindowSurfaceController对象;

  • 最后通过WindowSurfaceController将内部SurfaceControl拷贝给了outSurfaceControl,outSurfaceControl将返回给客户端的ViewRootImpl。

  WindowStateAnimator对象的作用是执行和管理WindowState动画和Surface操作,而WindowSurfaceController就是为WindowStateAnimator对象管理SurfaceControl对象。

  创建WindowSurfaceController对象如下:

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
    WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
        final WindowState w = mWin;
        // 如果已经存在,直接返回
        if (mSurfaceController != null) {
            return mSurfaceController;
        }
        // 设置WindowState#mHasSurface属性为false,表示没有Surface
        w.setHasSurface(false);
        // 重置绘制状态
        resetDrawState();
        // 
        int flags = SurfaceControl.HIDDEN;
        final WindowManager.LayoutParams attrs = w.mAttrs;
        // 计算Surface区域大小,relayout时,由于没有填充Frame,因此获取区域为0
        calculateSurfaceBounds(w, attrs, mTmpSize);

        try {
            ......
            // 创建WindowSurfaceController对象
            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
                    height, format, flags, this, windowType, ownerUid);
            mSurfaceFormat = format;
            // 设置WindowState#mHasSurface属性为false,表示没有Surface
            w.setHasSurface(true);
        } catch (OutOfResourcesException e) {
        }
        mLastHidden = true;

        return mSurfaceController;
    }

在创建WindowSurfaceController对象时,将会创建对应的SurfaceControl对象:

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java
    WindowSurfaceController(String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
        mAnimator = animator;                        // WindowStateAnimator对象
        mSurfaceW = w;                                        // Surface的宽,创建时为1, 在setBufferSizeInTransaction()中会设置具体值
        mSurfaceH = h;                                        // Surface的高,创建时为1
        title = name;                                        // 窗口名

        mService = animator.mService; // WMS对象
        final WindowState win = animator.mWin; // WindowState对象
        mWindowType = windowType; // Window类型
        mWindowSession = win.mSession; // IWindowSession对象
        mTmpTransaction = mService.mTransactionFactory.get();
        // 创建SurfaceControl
        final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setBufferSize(w, h)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, ownerUid)
                .setCallsite("WindowSurfaceController");
        mSurfaceControl = b.build();
    }

创建完成WindowSurfaceController对象后, 通过WindowState#setHasSurface()将WindowState#mHasSurface属性设置为true,表示该WindowState已经拥有了Surface。

2.7 WMS#updateFocusedWindowLocked()更新焦点窗口

  接下来,会通过WMS#updateFocusedWindowLocked()方法,进行焦点窗口的更新。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }

这里就是直接调用RootWindowContainer#updateFocusedWindowLocked():

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

updateFocusedWindowLocked()获取轮询RootWindowContainer容器中的对象,然后通过对象的DisplayContent去更新焦点窗口。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
            int topFocusedDisplayId) {
        // 查找当前焦点窗口
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        // 判断查找的结果和当前的焦点窗口是否一样
        if (mCurrentFocus == newFocus) {
            return false;
        }
        boolean imWindowChanged = false;
        // 输入法窗口
        final WindowState imWindow = mInputMethodWindow;
        if (imWindow != null) {
            final WindowState prevTarget = mInputMethodTarget;
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            imWindowChanged = prevTarget != newTarget;

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }

        if (imWindowChanged) {
            mWmService.mWindowsChanged = true;
            setLayoutNeeded();
            newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        }
        // 焦点窗口变化处理
        if (mCurrentFocus != newFocus) {
            mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
        }

        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
        final WindowState oldFocus = mCurrentFocus;
        mCurrentFocus = newFocus;
        mLosingFocus.remove(newFocus);

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

            if (newFocus.canReceiveKeys()) {
                // Displaying a window implicitly causes dispatching to be unpaused.
                // This is to protect against bugs if someone pauses dispatching but
                // forgets to resume.
                newFocus.mToken.paused = false;
            }
        }

        // 通知焦点窗口变化了
        onWindowFocusChanged(oldFocus, newFocus);

        int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);

        if (imWindowChanged && oldFocus != mInputMethodWindow) {
            // Focus of the input method window changed. Perform layout if needed.
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/,  updateInputWindows);
                focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
            } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                // Client will do the layout, but we need to assign layers
                // for handleNewWindowLocked() below.
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }

        if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            // The change in focus caused us to need to do a layout.  Okay.
            setLayoutNeeded();
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/, updateInputWindows);
            } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
                mWmService.mRoot.performSurfacePlacement();
            }
        }

        if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
            // If we defer assigning layers, then the caller is responsible for doing this part.
            getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
        }

        adjustForImeIfNeeded();

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

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

最后通过onWindowFocusChanged()去通知其他模块焦点窗口变化了。

2.8 设置MergedConfiguration对象给客户端

  接下来更新MergedConfiguration对象,MergedConfiguration是全局Configuration对象和override Configurationu对象的包装类,实现了Parcelable对象,因此可以用来进程间传递,它的主要作用就是WMS向ViewRootImpl同步Configuration。在relayoutWindow()时,会将当前global configuration 和override configuration给到MergedConfiguration,并创建出一个新的Configuration对象:

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void getMergedConfiguration(MergedConfiguration outConfiguration) {
        // 获取全局Configuration对象
        final Configuration globaluConfig = getProcessGlobalConfiguration();
        // 获取覆盖Configuration对象
        final Configuration overrideConfig = getMergedOverrideConfiguration();
        // 设置给MergedConfiguration
        outConfiguration.setConfiguration(globalConfig, overrideConfig);
    }

2.9 设置windowFrame和Inset给客户端

   WMS中的WindowFrame类,其中携带了一些影响窗口如何进行布局的Rect对象,比较重要的一些Rect对象有:

  • mDisplayFrame:来自于DisplayFrame#mUnrestricted属性,表示此窗口所在的ActivityStack的整个屏幕区域,一般等于逻辑屏大小;

  • mParentFrame:来自于mDisplayFrame,输入法、壁纸等特殊窗口除外,多数情况下,等于mDisplayFrame;

  • mContainingFrame:对于full-screen类型窗口来说,等于mParentFrame。

  这些Rect在DisplayPolicy#layoutWindowLw()方法中对它们进行了设置,具体流程在Surface的placement流程中说明。

  这里会将WindowFrame#mCompatFrame以及其他几个Rect对象设置给ViewRootImpl中传入的变量,从而输出给ViewRootImpl:

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
    void getCompatFrame(Rect outFrame) {
        outFrame.set(mWindowFrames.mCompatFrame);
    }

    void getInsetsForRelayout(Rect outContentInsets, Rect outVisibleInsets,
            Rect outStableInsets) {
        outContentInsets.set(mWindowFrames.mContentInsets);
        outVisibleInsets.set(mWindowFrames.mVisibleInsets);
        outStableInsets.set(mWindowFrames.mStableInsets);

        mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
    }

3.总结

  1. 通过relayout操作,更新对应WindowState的大小、可见性等相关属性;

  2. 在第一次relayout时,就会创建一个Surface,并对Surface进行大小等属性的设置,返回给客户端,作为View显示内容的载体;

  3. 创建Surface时,通过WindowStateAnimator中的WindowSurfaceController进行创建。

  Surface创建好之后,返回给可客户端,客户端会将具体内容的画在Surface上,并在完成后通知WMS。WMS中将通过WindowSurfacePlacer#performSurfacePlacement()方法,最终将Surface送给SurfaceFlinger,完成显示。

  以上整个过程时序图如下:

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

推荐阅读更多精彩内容