Android View的绘制原理

作者: 奔跑吧李博 | 来源:发表于2023-11-07 22:51 被阅读0次
    Android的Activity界面从何处开始绘制

    1.设置WindowManager和DecorView的关系
    2.把DecorView加入到WindowManager当中

    3.创建了一个ViewRootImpl对象root

    root = new ViewRootImpl(view.getContext(), display);
    
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
    
                // do this last because it fires off messages to start doing things
                try {
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
    

    ViewRootImpl 是顶层视图,当我们调用 view 的 requestLayout 或者 invalidate 最终都会循环调用到 ViewRootImpl 中。在 onCreate 调用流程时,DecorView 中的布局已经创建出来,而 ViewRootImpl 在 onResume 之后才会创建。

    setView方法中调用了requestLayout()方法:

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

    到这里,好了,界面绘制从这里开始:

    void checkThread() {
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }
    

    这里就是要求界面绘制必须在主线程中进行更新mThread就是主线程,所以如果当前运行的线程不是主线程,那么就直接报这个异常

    然后就是真正的界面绘制了:

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

    最终会调用performTraversals方法,然后就开始View的measure了,在ViewRootImpl中是去measure前面提到的DecordeView

    measure阶段

    调用performMeasure方法,在performMeasure方法中调用了View的measure方法。

    layout阶段

    对View进行measure完成之后(measureHierarchy方法)会再执行performLayout(lp, mWidth, mHeight)

    draw阶段

    draw操作在performTraversals方法里交给performDraw去执行,然后会在调用DecorView的draw方法。
    对于ViewGroup来说dispatchDraw就是去绘制子 View,具体实现在drawChild方法:

    总结view绘制流程

    参考:
    https://blog.51cto.com/u_16099278/6900766
    https://zhuanlan.zhihu.com/p/596265459

    由 View 绘制流程引出的一些常见问题
    • onCreate 中子线程更新 UI 为什么不会报错

    DecorView 在 onCreate 时就创建好了,那 ViewRootImpl 呢?在 onResume 之后才会报错,如果子线程更新UI执行的时机在 ViewRootImpl 未创建之前,那么更新UI触发的 requestLayout 只会回调到布局的根 ViewGroup 中,他们的 requestLayout 并没有检查线程这一操作。
    而在 onResume 之后,ViewRootImpl 的 setView 方法对 DecorView 进行了 assignParent 操作,成为了顶层视图,之后再触发 requestLayout 会进行线程检查。

    相关文章

      网友评论

        本文标题:Android View的绘制原理

        本文链接:https://www.haomeiwen.com/subject/wvwfwdtx.html