Android View的绘制流程(四)-Activity视图-ViewRootImpl

1,005 阅读3分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

相关文章:
Android View的绘制流程(一)-绘制流程以及Activity视图介绍
Android View的绘制流程(二)-Activity视图-DecorView
Android View的绘制流程(三)-Activity视图-WindowManager
Android View的绘制流程(四)-Activity视图-ViewRootImpl
Android View的绘制流程(五)-Measure
Android View的绘制流程(六)-Layout
Android View的绘制流程(七)-Draw

上一篇文章,介绍了窗口和窗口管理Window和WindowManager,DecorView创建完成后,传递给WindowManager,通过windowManager来管理,最终是通过调用addView方法,实例化了 ViewRootImpl ,同时调用 ViewRootImpl 的 setView 方法来持有了 DecorView。上一篇结尾也说到View 的绘制是由 ViewRootImpl 来负责的,每个应用程序窗口的 DecorView 都有一个与之关联的 ViewRootImpl 对象,这种关联关系是由 WindowManager 来维护的。那么接下来我们介绍ViewRootImpl,是怎么来绘制View的。

ViewRootImpl

先看 ViewRootImpl 的 setView 方法,代码很多,这里截取重要部分

/**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......
               
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

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

上面代码可以看到,使用mView保存了传递过来的DecorView,然后通过requestLayout 方法更新UI

public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

绘制UI是在主线程,所以checkThread 主要是做一个校验。接着调用 scheduleTraversals 开始计划绘制了。

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

这里使用到一个同步屏障mTraversalBarrier
同步屏障:Handler 的同步屏障。它的作用是可以拦截 Looper 对同步消息的获取和分发,加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态。也就是说,对 View 绘制渲染的处理操作可以优先处理。
mTraversalRunnable一个Runnable,最终里面会调用run方法,方法里调用了doTraversal方法,doTraversal字面意思就可以理解,开始View的绘制了:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    
    
 void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

从上面方法可以看出来,该方法内部最终会调用 performTraversals 进行绘制。 performTraversals方法相信大家多少都听说过,这个方法就是用来绘制View的,依次就会调用我们非常熟悉的measure,layout和draw方法了。

// ViewRootImpl 类
private void performTraversals() {
    // 这个方法代码非常多,但是重点就是执行这三个方法
    // 执行测量
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    // 执行布局(ViewGroup)中才会有
    performLayout(lp, mWidth, mHeight);
    // 执行绘制
    performDraw();
}

到此就把activity视图所涉及到重要类都介绍了,剩下的就是View的绘制了,接下来的文章会重点介绍,总结下,如下图:

1111.png

View的整个绘制流程如上图所示,也就是以上几篇文章介绍的重点,大家可以下载Activity的源码,一层层的进入查看,就会了解这个整体的过程了,源码真是好东西,读懂了源码,可以帮我们解决很多很多问题,下面会介绍View绘制过程中调用的三个重要方法,也是以后我们做自定义View过程中,经常会用到的方法。

以上介绍有不对的地方还请指正,欢迎留言评论。