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




一.回到梦开始的地方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.关于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();
                        	
           ...
        }
        ...
}

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;
    }
//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);
                ...
                }
            	...
       
            }



三.写在最后

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
作者aliouswang,源码FriendRefreshView,微信朋友圈我们都经常用,朋友圈的下拉刷新比较有意思,我们今天将要模仿打造微信朋友圈的下拉刷新控件,当然微信的这种刷新设计可能不是最好的,实际项目中你可以用V4包里面的SwipeRefreshView或者Chris Banes的AndroidPullRerfresh,看产品经理的设计。 思路 我们初步分析下,界面上主要有二个控件,一个彩虹状的圆形LoadingView,一个是ListView,那么我大致可以有下面三个步骤: 第一步:需要自定义一个ViewGroup,把上面的2个控件add进来。 第二步:利用ViewDragHelper处理控件拖动。当ListView处于顶部时,如果继续向下拖动,就拦截触摸事件,将触摸事件传递给ViewDragHelper处理,这里比较关键,主要是是否拦截触摸事件的判断条件要处理好,否则如果ListView的点击和滚动事件被我们拦截了,那就悲剧了。 第三步:在ViewDragHelper的拖动回调方法里面,设置listView和彩虹LoadingView的位置,调用requestLayout。 第四步:手势松开后,开始刷新,LoadingView在固定位置做旋转动画。 第五步:如果设置了onRefreshListener,执行onRefresh接口。 第六步:调用stopRefresh,完成刷新,这一步需要控件使用者手动去调用,控件本身不自动触发。 文/ALIOUS(简书作者) 原文链接:http://www.jianshu.com/p/1ca0caf5fd8b 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值