• Android R窗口Window的requestlayout过程大揭秘


        Android R窗口Window的requestlayout过程大揭秘


    Android应用程序窗口设计系列博客:

    Android应用程序窗口设计之Window及WindowManager的创建
    Android应用程序窗口设计之setContentView布局加载的实现
    普法Android的Token前世今生以及在APP,AMS,WMS之间传递
    Android应用程序窗口设计之窗口的添加
    Android应用程序窗口设计之建立与WMS服务之间的通信过程
    Android窗口设计之Dialog、PopupWindow、系统窗口的实现
    Android应用程序建立与AMS服务之间的通信过程




    引言

      该系列博客尘封很久很久了,是时候该再续前缘,重新上路了!我挑着担啊,你牵着马!好了言归正传,好记得前面的博客Android应用程序窗口设计之窗口的添加,我们重点分析了应用端通过一顿猛如虎一样的操作之后请求到了WMS的addWindow方法,然后在WMS方法中为应用端窗口在WMS端建立的对应的关系,最终建立了下面的的对应关系图:

    在这里插入图片描述

    在前面的博客的Android应用程序窗口设计之窗口的添加的2.6章节,我们对requestLayout()只是简单的描述了下它(这里调用异步刷新请求,最终会调用performTraversals方法来完成View的绘制在向WMS添加窗口前进行UI布局)。过了这么久了,是时候把它捡起来好好分析分析了!朋友们药不能停啊!好了开干!

    时间如梭,写该系列博客还是在很久之前,并且使用的还是Android 7.1源码,这里为了跟上时代的进步,所以做出一个重大决定,该系列后续博客分析都将会在Android 11(R)上的基础分析!

    注意:本篇的介绍是基于Android 11.xx平台为基础的,其中涉及的代码路径如下:

    frameworks/base/core/java/android/view/
    	--- WindowManager.java
     	--- View.java
    	--- ViewManager.java
    	--- ViewRootImpl.java
    	--- Window.java
    	--- Display.java
    	--- WindowManagerImpl.java
    	--- WindowManager.java
    	--- WindowManagerGlobal.java
    	--- IWindowManager.aidl
    	--- IWindow.aidl
    	--- IWindowSession.aidl
    	--- Surface.java
    	--- SurfaceControl.java
    
    frameworks/base/services/core/java/com/android/server/wm/
    	--- WindowManagerService.java
    	--- AppWindowToken.java
    	--- WindowState.java
    	--- Session.java
    	--- WindowToken.java
    	--- WindowStateAnimator.java
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24



    一.回到梦开始的地方ViewRootImpl.requestlayout()

    还记得以前Android面试经常会问的Android自定义控件 requestLayout / invalidate的区别吗?如果用一句话来概括就是requestLayout 是用来设置 FORCE_LAYOUT 标志,invalidate 用来设置 dirty 标志。所以 requestLayout 会触发 measure 和 layout,invalidate 只会触发 draw。而自定义空间的requestLayout方法最终会触发ViewRootImpl.requestlayout()方法,这里我们来好好盘盘它!

      好了,不多说!直接上源码,开撸才够味!

     //frameworks/base/core/java/android/view/ViewRootImpl.java
    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
            ...
            
        @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                /**
                 * 此处重点标记,为啥说UI操作不再在子线程中,这个地方就是答案
                 */
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }
    
    
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                //移除同步屏障
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    			...
                performTraversals();//详见章节1.1
    			...
            }
        }
    
    	final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    	    final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
        @UnsupportedAppUsage
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                //设置一个同步屏障
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                /**
                * 向Choreographer设置sync监听,监听Vsync信号,然后发送异步消息 -> 执行绘制任务
                * 注意此处并不会立马执行,而是要等到vsync信号的到来才会执行
                **/
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    1.关于Handler同步屏障,我们这里只需要知道设置了同步屏障之后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。我们在调用requestLayout()方法之后,并不会马上开始进行绘制任务,而是会给主线程设置一个同步屏幕,并设置Vsync信号监听。当Vsync信号的到来,会发送一个异步消息到主线程Handler,执行我们上一步设置的绘制监听任务,并移除同步屏障。并且我们通常使用Handler发送的都是同步消息。


    2.关于Choreographer编舞者,这里也不重点介绍,我们这里只需要知道它是一种Android的机制,是Android系统为了通知应用端可以进行绘制的一种机制。关于它,如果后面有机会,我们可以重点熟悉熟悉了解它!


    1.1…ViewRootImpl.performTraversals()拉开应用绘制三部曲的章程

      虽然这里我提到了了应用绘制三部曲,但是很遗憾我们今天的重点也不是它,我们今天的重点是ViewRootImpl,如何向WMS服务请求relayoutWindow布局,主要是填充Surface,构建对应的Window的BufferLayer。好了,不多说了,我们直接看代码!

     //frameworks/base/core/java/android/view/ViewRootImpl.java
    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
            ...
    
        /**
         * 向WMS发起布局请求
         */
        private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                boolean insetsPending) throws RemoteException {
                ...
            //最终调用到WMS,向WMS发起布局请求,详见章节二
            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);            
                ...
       	}
    
            private void performTraversals() {
            	...
                /**
                 * 向WMS服务请求relayoutWindow布局,主要是填充Surface,构建对应的Window的BufferLayer
                 * 详见章节二
                 **/
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
               	...
       	        // Ask host how big it wants to be
                //绘制三部曲之测量
               performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
               ...
                //绘制三部曲之布局
               performLayout(lp, mWidth, mHeight);
               ...
               //绘制三部曲之开始绘制
               performDraw();
                            	
               ...
            }
            ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    performTraversals的四大步骤,每一步都很重要,如果要详细捯饬清楚真的是没有个三天三夜是真的搞不清楚,但是这里我们仅仅只重点关心relayoutWindow()向WMS发起布局请求。而这里的通信的关键Session的类关系如下,而我们这里的mWindowSession是Session的代理端,所以它最终会调用到Session的服务端Session。
    在这里插入图片描述




    二.WMS开启处理应用端发起的布局请求

      Android不愧是是C/S架构的集大成者,应用端的相关操作基本兜兜转转最后都会到达服务端,这里也不例外。这个章节我们来开始分析WMS怎么处理应用端发起的布局请求。

    //frameworks/base/services/core/java/com/android/server/wm//Session.java
        @Override
        public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
                Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
                Rect outStableInsets, Rect outBackdropFrame,
                DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
                SurfaceControl outSurfaceControl, InsetsState outInsetsState,
                InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
                SurfaceControl outBLASTSurfaceControl) {
    		...
    		//这里的mService是WMS的实例对象
            int res = mService.relayoutWindow(this, window, seq, attrs,
                    requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                    outFrame, outContentInsets, outVisibleInsets,
                    outStableInsets, outBackdropFrame, cutout,
                    mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls,
                    outSurfaceSize, outBLASTSurfaceControl);
    		..
            return res;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    //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    BLAST是Android的一个新的模式,这里暂且不分析
         * @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) {
                	...
                	if (shouldRelayout) {
    				...
                        //创建Surface,这里会调用到SurfaceFlinger
                        //这个章节后面会有抓们的博客来分析
                        result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
                                result, win, winAnimator);
                    ...
                    }
                	...
           
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46



    三.写在最后

      到这里本篇博客Android R窗口Window的requestlayout过程大揭秘真的就宣告结束了,相信很多读者看到这篇博客,会有一个感觉是不是我的技术和写作水平退步了,咋感觉这篇博客写了,咋感觉又没有写。是的我也有这个感觉,因为我这篇博客是为了后续引出应用Surface创建而添加的一个博客,所以写得简单也是清理之中的,因为该篇博客是一个过渡主要是为了后续的博客不唐突而添加的。望各位见谅!欢迎读者继续关注!好了,青山不改绿水长流先到这里了。如果本博客对你有所帮助,麻烦关注或者点个赞,

  • 相关阅读:
    Python random 模块
    C++ 性能小测 1 二维数组的遍历效率
    em,rem,px,rpx的区别与使用
    ESP8266-Arduino编程实例-BMP180气压温度传感器驱动
    【微信小程序】后台数据交互于WX文件使用
    golang 泛型详解
    【新版】系统架构设计师 - 案例分析 - 数据库设计
    数据结构专题 | 先序非递归遍历二叉树
    强化学习的一周「GitHub 热点速览」
    [山东科技大学OJ]2676 Problem G: 数字统计二
  • 原文地址:https://blog.csdn.net/tkwxty/article/details/127799832