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

既上一篇博客,这篇我们分析WMS的relayoutWindow函数。

relayoutWindow

我们先看下relayoutWindow函数

    public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
            Surface outSurface) {
        ......

        synchronized(mWindowMap) {
            WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (viewVisibility != View.GONE && (win.mRequestedWidth != requestedWidth
                    || win.mRequestedHeight != requestedHeight)) {
                win.mLayoutNeeded = true;
                win.mRequestedWidth = requestedWidth;
                win.mRequestedHeight = requestedHeight;
            }

           ......

            final boolean scaledWindow =
                ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);

            if (scaledWindow) {
                // requested{Width|Height} Surface's physical size
                // attrs.{width|height} Size on screen
                win.mHScale = (attrs.width  != requestedWidth)  ?
                        (attrs.width  / (float)requestedWidth) : 1.0f;
                win.mVScale = (attrs.height != requestedHeight) ?
                        (attrs.height / (float)requestedHeight) : 1.0f;
            } else {
                win.mHScale = win.mVScale = 1;
            }

            ......

            win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
            configChanged = updateOrientationFromAppTokensLocked(false);
            performLayoutAndPlaceSurfacesLocked();
            .....
            
            outFrame.set(win.mCompatFrame);
            outOverscanInsets.set(win.mOverscanInsets);
            outContentInsets.set(win.mContentInsets);
            outVisibleInsets.set(win.mVisibleInsets);
            outStableInsets.set(win.mStableInsets);
            outOutsets.set(win.mOutsets);

            inTouchMode = mInTouchMode;
            ......
        }
        ......

        return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
                | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
                | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0);
    }

参数client是一个Binder代理对象,它引用了运行在应用程序进程这一侧中的一个W对象,用来标志一个Activity窗口。在应用程序进程这一侧的每一个W对象,在WindowManagerService服务这一侧都有一个对应的WindowState对象,用来描述一个Activity窗口的状态。因此,WindowManagerService类的成员函数relayoutWindow首先通过调用另外一个成员函数windowForClientLocked来获得与参数client所对应的一个WindowState对象win,以便接下来可以对它进行操作。

        本文我们只关注WindowManagerService类的成员函数relayoutWindow中与窗口大小计算有关的逻辑,计算过程如下所示:

        1. 参数requestedWidth和requestedHeight描述的是应用程序进程请求设置Activity窗口中的宽度和高度,它们会被记录在WindowState对象win的成员变量mRequestedWidth和mRequestedHeight中。

        2. WindowState对象win的成员变量mAttr,它指向的是一个WindowManager.LayoutParams对象,用来描述Activity窗口的布局参数。其中,这个WindowManager.LayoutParams对象的成员变量width和height是用来描述Activity窗口的宽度和高度的。当这个WindowManager.LayoutParams对象的成员变量flags的WindowManager.LayoutParams.FLAG_SCALED位不等于0的时候,就说明需要给Activity窗口的大小设置缩放因子。缩放因子分为两个维度,分别是宽度缩放因子和高度缩放因子,保存在WindowState对象win的成员变量HScale和VScale中,计算方法分别是用应用程序进程请求设置Activity窗口中的宽度和高度除以Activity窗口在布局参数中所设置的宽度和高度。

        3. 参数insetsPending用来描述Activity窗口是否有额外的内容区域边衬和可见区域边衬未设置,它被记录在WindowState对象win的成员变量mGivenInsetsPending中。

        4. 调用WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLocked来计算Activity窗口的大小。计算完成之后,参数client所描述的Activity窗口的大小、内容区域边衬大小和可见区域边边衬大小就会分别保存在WindowState对象win的成员变量mFrame、mContentInsets和mVisibleInsets中。

        5. 将WindowState对象win的成员变量mFrame、mContentInsets和mVisibleInsets的值分别拷贝到参数出数outFrame、outContentInsets和outVisibleInsets中,以便可以返回给应用程序进程。

        经过上述五个操作后,Activity窗口的大小计算过程就完成了,接下来我们继续分析WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLocked的实现,以便可以详细了解Activity窗口的大小计算过程。


performLayoutAndPlaceSurfacesLocked

我们再来看performLayoutAndPlaceSurfacesLocked函数,直接调用了performLayoutAndPlaceSurfacesLockedLoop函数。

    private final void performLayoutAndPlaceSurfacesLocked() {
        int loopCount = 6;
        do {
            mTraversalScheduled = false;
            performLayoutAndPlaceSurfacesLockedLoop();
            mH.removeMessages(H.DO_TRAVERSAL);
            loopCount--;
        } while (mTraversalScheduled && loopCount > 0);
        mInnerFields.mWallpaperActionPending = false;
    }

performLayoutAndPlaceSurfacesLockedLoop函数:

    private final void performLayoutAndPlaceSurfacesLockedLoop() {
        if (mInLayout) {
            return;
        }

        if (mWaitingForConfig) {
            return;
        }

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

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

        boolean recoveringMemory = false;
        if (!mForceRemoves.isEmpty()) {
            recoveringMemory = true;
            // Wait a little bit for things to settle down, and off we go.
            while (!mForceRemoves.isEmpty()) {
                WindowState ws = mForceRemoves.remove(0);
                Slog.i(TAG, "Force removing: " + ws);
                removeWindowInnerLocked(ws);
            }

            Object tmp = new Object();
            synchronized (tmp) {
                try {
                    tmp.wait(250);
                } catch (InterruptedException e) {
                }
            }
        }

        try {
            performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);

            mInLayout = false;

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

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

        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
    }

从WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLocked的名称可以推断出,它执行的操作绝非是计算窗口大小这么简单。计算窗口大小只是其中的一个小小功能点,它主要的功能是用来刷新系统的UI。在我们这个情景中,为什么需要刷新系统的UI呢?Activity窗口在其属性发生了变化,例如,可见性、大小发生了变化,

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值