Android开发经验谈Android开发Android技术知识

Android View的绘制原理

2023-11-07  本文已影响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 绘制流程引出的一些常见问题

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

上一篇下一篇

猜你喜欢

热点阅读