Android14 WMS-窗口绘制之relayoutWindow流程(一)-Client端

Android14 WMS-窗口添加流程(一)-Client端-CSDN博客

Android14 WMS-窗口添加流程(二)-Server端-CSDN博客

经过上述两个流程后,窗口的信息都已经传入了WMS端。

1. ViewRootImpl#setView

在窗口添加流程(一)中,有这个方法:

http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/view/ViewRootImpl.java#1314

java 复制代码
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
...
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
...
    }

2. ViewRootImpl#requestLayout

requestLayout中的scheduleTraversals是一个异步方法

java 复制代码
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
//异步方法
            scheduleTraversals();
        }
    }

3. ViewRootImpl#scheduleTraversals

scheduleTraversals中有一个Runnable方法

关于Choreographer编舞者,这里也不重点介绍。

java 复制代码
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
//执行view遍历操作,进行measure,layout,draw操作
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//Choreographer Posts a callback to run on the next frame.
// The callback runs once then is automatically removed.
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

4. ViewRootImpl#doTraversal

来看看Runnable中的方法

java 复制代码
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
//要执行到了真正的遍历操作,这就要对view执行measure,layout, draw流程了
            performTraversals();
...
        }
    }

5. ViewRootImpl#performTraversals

java 复制代码
    private void performTraversals() {
...
        // cache mView since it is used so much below...
//这个mView是通过setView方法传进来的,也就是Activity的根布局DecorView,使用final修饰,以防在遍历过程中被修改
        final View host = mView;
...
//mAdded指DecorView是否被成功加入到window中,在setView()中被赋值为true
        if (host == null || !mAdded) {
            mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
            return;
        }
...
        mIsInTraversal = true;//是否正在遍历
        mWillDrawSoon = true;//是否需要马上绘制
        boolean cancelDraw = false;
        String cancelReason = null;
        boolean isSyncRequest = false;

        boolean windowSizeMayChange = false;
        WindowManager.LayoutParams lp = mWindowAttributes;
//顶层视图DecorView窗口的期望宽高
        int desiredWindowWidth;
        int desiredWindowHeight;
//DecorView是否可见
        final int viewVisibility = getHostVisibility();
//视图可见性改变
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                // Also check for possible double visibility update, which will make current
                // viewVisibility value equal to mViewVisibility and we may miss it.
                || mAppVisibilityChanged);
...
        WindowManager.LayoutParams params = null;
...
        boolean windowShouldResize = layoutRequested && windowSizeMayChange
            && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
                || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.width() < desiredWindowWidth && frame.width() != mWidth)
                || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.height() < desiredWindowHeight && frame.height() != mHeight));
        windowShouldResize |= mDragResizing && mPendingDragResizing;
...
//第一次执行测量布局绘制操作||Activity窗口大小需要改变||View的可见性发生了变化||窗口属性发生了变化||ViewRootHandler接收到消息MSG_RESIZED_REPORT,即size改变了
        if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
                || mForceNextWindowRelayout) {
...
//如果此窗口为窗口管理器提供内部insets,那么我们首先要在布局期间使提供的insets保持不变。
//这样可以避免它短暂地导致其他窗口根据窗口的原始框架调整大小/移动,
//等到我们完成此窗口的布局并返回窗口管理器,并最终计算出插图。
            insetsPending = computesInternalInsets;
...
//判断是否有surface
            boolean hadSurface = mSurface.isValid();

            try {
...
                if (mFirst || viewVisibilityChanged) {
                    mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED;
                }
//params,窗口属性变化内容
//请求WMS计算Activity窗口大小及边衬区域大小
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
   	        // Ask host how big it wants to be
            //绘制三部曲之measure
           performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
           ...
            //绘制三部曲之layout
           performLayout(lp, mWidth, mHeight);
           ...
           //绘制三部曲之draw
           performDraw();
...

6. ViewRootImpl#relayoutWindow

我们主要是来看看ViewRootImpl如何向WMS申请布局的

java 复制代码
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
...
//window申请的宽
        final int requestedWidth = (int) (measuredWidth * appScale + 0.5f);
//window申请的高
        final int requestedHeight = (int) (measuredHeight * appScale + 0.5f);
        int relayoutResult = 0;
        mRelayoutSeq++;
        if (relayoutAsync) {
            mWindowSession.relayoutAsync(mWindow, params,
                    requestedWidth, requestedHeight, viewVisibility,
                    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
                    mLastSyncSeqId);
        } else {
//请求重新布局
            relayoutResult = mWindowSession.relayout(mWindow, params,
                    requestedWidth, requestedHeight, viewVisibility,
                    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
                    mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
                    mTempInsets, mTempControls, mRelayoutBundle);
...

这里就又用到了AIDL,WindowSession,WindowSession是APP和WMS沟通的桥梁

复制代码
   final IWindowSession mWindowSession;

可以看下这篇文章加强理解

Android14 WMS-IWindowSession介绍-CSDN博客

7. Session #relayout

java 复制代码
//Session继承了IWindowSession.Stub
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
...
    @Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
            int lastSyncSeqId, ClientWindowFrames outFrames,
            MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl,
            InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
            Bundle outSyncSeqIdBundle) {
        if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                + Binder.getCallingPid());
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
//调用到了Server端
        int res = mService.relayoutWindow(this, window, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, seq,
                lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSyncSeqIdBundle);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                + Binder.getCallingPid());
        return res;
    }

8. WindowManagerService #relayoutWindow

Server端流程太多了,另起一篇文章分析。

相关推荐
天空中的野鸟37 分钟前
Android音频采集
android·音视频
小白也想学C2 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程2 小时前
初级数据结构——树
android·java·数据结构
闲暇部落4 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX6 小时前
Android 分区相关介绍
android
大白要努力!7 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee7 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood7 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-10 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen12 小时前
MTK Android12 user版本MtkLogger
android·framework