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端流程太多了,另起一篇文章分析。

相关推荐
2501_915921431 小时前
iOS 是开源的吗?苹果系统的封闭与开放边界全解析(含开发与开心上架(Appuploader)实战)
android·ios·小程序·uni-app·开源·iphone·webview
allk552 小时前
OkHttp源码解析(一)
android·okhttp
allk552 小时前
OkHttp源码解析(二)
android·okhttp
2501_915909065 小时前
原生 iOS 开发全流程实战,Swift 技术栈、工程结构、自动化上传与上架发布指南
android·ios·小程序·uni-app·自动化·iphone·swift
2501_915909065 小时前
苹果软件混淆与 iOS 代码加固趋势,IPA 加密、应用防反编译与无源码保护的工程化演进
android·ios·小程序·https·uni-app·iphone·webview
2501_916007475 小时前
苹果软件混淆与 iOS 应用加固实录,从被逆向到 IPA 文件防反编译与无源码混淆解决方案
android·ios·小程序·https·uni-app·iphone·webview
介一安全5 小时前
【Frida Android】基础篇6:Java层Hook基础——创建类实例、方法重载、搜索运行时实例
android·java·网络安全·逆向·安全性测试·frida
沐怡旸8 小时前
【底层机制】【Android】深入理解UI体系与绘制机制
android·面试
啊森要自信8 小时前
【GUI自动化测试】YAML 配置文件应用:从语法解析到 Python 读写
android·python·缓存·pytest·pip·dash
下位子10 小时前
『AI 编程』用 Codex 开发识字小帮手应用
android·openai·ai编程