【View的绘制流程】绘制入口

View的绘制流程

一、前言

下面这个概念,相信大家都耳熟能详了:

view 的绘制流程主要分为三个阶段:measure、layout、draw

measure:根据父view传递的MeasureSpec计算子View大小

layout:根据measure子view所得到的布局大小和布局参数,将子view放在合适的位置上。

draw:把view对象绘制到屏幕上。

但是,你知道DecorView是怎样添加到PhoneWindow的吗?DecorView又是从那里开始执行绘制的?
本篇幅就带大家从源码的角度分析一下view的绘制入口,下面我从源码的角度看一下这个流程。

在这之前,先要清楚一个概念。一个 Activity 包含一个Window,Window的实现类是PhoneWindow。一个PhoneWindow又会包含一个 DecorView ,DecorView是ViewTree里面的顶层布局,是继承于FrameLayout。

activity通过 setContent()方法加载布局的时候是将布局加载到DecorView 的ContentView上。

二、绘制入口

下面进入正题,我们先看看顶层视图DecorView是怎样被添加到PhoneWindow上的。

这就得从activity的启动说起了,当Activity初始化window和将布局添加到DecorView类之后,ActivityThread类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口中。

ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main方法是一个APP的真正入口,MainLooper在它的main方法中被创建。

ActivityThread.java:
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {

            ..................

            if (r.window == null && !a.mFinished && willBeVisible) {
                //获得当前Activity的PhoneWindow对象
                r.window = r.activity.getWindow();
                //获得当前phoneWindow内部类DecorView对象
                View decor = r.window.getDecorView();
                //设置窗口顶层视图DecorView可见度
                decor.setVisibility(View.INVISIBLE);
                //得当当前Activity的WindowManagerImpl对象
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    //标记根布局DecorView已经添加到窗口
                    a.mWindowAdded = true;
                    //将根布局DecorView添加到当前Activity的窗口上面
                    wm.addView(decor, l);

            .....................

将根布局DecorView添加到当前Activity的窗口上,wm.addView(decor, l); 这行代码的wm指的是WindowManagerImpl类,在WindowManagerImpl的addView中,又会调用mGlobal.addView(view, params, mDisplay, mParentWindow);这行代码,其中,mGlobal对象是WindowManagerGlobal类,在WindowManagerGlobal的addView方法中,会创建一个ViewRootImpl对象root,然后调用ViewRootImpl类中的setView方法。

WindowManagerGlobal类: 
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

        ............

        ViewRootImpl root;
        View panelParentView = null;

        ............

        //获得ViewRootImpl对象root
         root = new ViewRootImpl(view.getContext(), display);

        ...........

        // do this last because it fires off messages to start doing things
        try {
            //将传进来的参数DecorView设置到root中
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
          ...........
        }
    }

下面继续往下看WindowManagerGlobal类的ViewRootImpl.setView()方法,方法中,将传进来的DecorView对象赋值给全局的mView,标记已添加了DecorView,然后调用requestLayout()请求布局。

ViewRootImpl类:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
            //将顶层视图DecorView赋值给全局的mView
                mView = view;
            .............
            //标记已添加DecorView
             mAdded = true;
            .............
            //请求布局
            requestLayout();

            .............     
        }
 }

接下来,继续进入ViewRootImpl的requestLayout()方法,看看里面做了啥:

ViewRootImpl类:
@Override
public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    ................

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
        }
    }

..............

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

...............

 void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);

            try {
                performTraversals();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    }

............

requestLayout()方法中,会再调用scheduleTraversals(),然后会执行doTraversal()方法,最终在doTraversal()方法中调用了performTraversals()方法。所以,DecorView的绘制会进入到ViewRootImpl类中的performTraversals()方法执行

下面再看看performTraversals方法又做了啥:

ViewRootImpl类:
private void performTraversals() {
        //mView就是DecorView根布局
        final View host = mView;
        //成员变量mAdded已被赋值为true,因此条件不成立
        if (host == null || !mAdded)
            return;
        //是否正在遍历
        mIsInTraversal = true;
        //是否马上绘制View
        mWillDrawSoon = true;

        .............
        //顶层视图DecorView所需要窗口的宽度和高度
        int desiredWindowWidth;
        int desiredWindowHeight;

        .....................
        //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView
        if (mFirst) {
            mFullRedrawNeeded = true;
            mLayoutRequested = true;
            //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏
            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
                // NOTE -- system code, won't try to do compat mode.
                Point size = new Point();
                mDisplay.getRealSize(size);
                desiredWindowWidth = size.x;
                desiredWindowHeight = size.y;
            } else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高
                DisplayMetrics packageMetrics =
                    mView.getContext().getResources().getDisplayMetrics();
                desiredWindowWidth = packageMetrics.widthPixels;
                desiredWindowHeight = packageMetrics.heightPixels;
            }
    }
............
 //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
 
  //执行测量操作
  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

........................
  //执行布局操作
  performLayout(lp, desiredWindowWidth, desiredWindowHeight);

.......................
  //执行绘制操作
  performDraw();
}

可以看到,在performTraversals方法中,最终调用performMeasure、performLayout、performDraw这三个方法,分别执行测量、布局和绘制操作。

下面我们做个总结:

在Activity启动过程中,会执行到onResume生命周期,这个过程会执行到ActivityThreadhandleResumeActivity方法,

handleResumeActivity方法中,会执行WindowManagerImpl.addView方法,进而执行WindowManagerGlobal.addView方法,最终在WindowManagerGlobal.addView方法中,会创建一个ViewRootImpl对象,并调用ViewRootImpl.setView方法,将DecorView赋值给ViewRootImpl的mView这个成员变量,将根布局DecorView添加到Window上,然后执行requestLayout()方法,requestLayout()方法又会执行到scheduleTraversals()方法,

scheduleTraversals()方法中,会通过Choreographer这个类post一个TraversalRunnable,并在下一个VSync信号到来时,才会执行TraversalRunnable,TraversalRunnable的run方法,会执行doTraversal()方法,然后执行performTraversals()方法,从而执行performMeasureperformLayoutperformDraw这三大流程。

由上可知,DecorView添加到PhoneWindow上并进入绘制的流程是这样的:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一场雪ycx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值