Andriod-View绘制流程

Andriod-View绘制流程


学而不思则罔,思而不学则殆


本文中的源码是基于API29的,即Android11系统版本


前言

先看一个简单的布局。
在这里插入图片描述
布局查看工具:Tools–>Layout Inspector

在这里插入图片描述
通过:Android\Sdk\tools\bin\uiautomatorviewer.bat查看布局

其中这个android:id/content的FrameLayout就是Activity中setContentView(R.layout.activity_main)的父布局
在这里插入图片描述
通过Hierarchy Viewer(布局树查看器)

在这里插入图片描述

  1. DecorView是根布局
  2. ContentFrameLayout是Activity中setContentView(R.layout.activity_main)的父布局

通过前面的图片,我们知道了View家族的结构。而View绘制就是从DecorView跟布局开始。
在自定义View的时候一般需要重写父类的onMeasure()、onLayout()、onDraw()三个方法,来完成视图的展示过程。当然更复杂的工作都要系统代劳了,自定义的工作相对比较简单。一个完整的绘制流程是要包含measure、layout、draw这三个步骤的。

measure:测量。系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。
layout:布局。根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。
draw:绘制。确定好位置后,就将这些控件绘制到屏幕上。

本篇文章只讲绘制流程,具体怎么测量,怎么布局,怎么绘制在后续的文章中会在讲


绘制流程

View绘制的UML时序图
在这里插入图片描述

1

    //ActivityThread.java
    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
        unscheduleGcIdler();
        ...
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ...
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        wm.addView(decor, l);
        ...
    }

这里的decor就是前文说的DecorView;ViewManager是WindowManagerImpl;
在Activity执行performResumeActivity以后,会执行ViewManager.addView()方法,将DecorView添加到Window上。由此可以得到:View的绘制是在Activity onResume()之后进行的。

2

    //WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
        ...
        ViewRootImpl root;
        ...
        root = new ViewRootImpl(view.getContext(), display);
        ...
        root.setView(view, wparams, panelParentView);
        ...

3

    //ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
                ...
                requestLayout();
                ...
        }
    }
    
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    //这个方法大家应该比较熟悉,当我们在子线程更新UI的时候就会由这个方法抛出异常
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

checkThread这个方法我想大家应该很熟悉,开发中被它奇袭过很多次。

4
这个方法很重要,这里开始的时候发送了一个屏障方法,发送一个铜须消息,这个时候MessageQueue中的异步消息就不能处理,被阻塞掉,只有屏障消息被移除后才能处理其他消息。
屏障消息可以欢迎查看文章Andriod-消息机制Handler

    //ViewRootImpl.java
    @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //设置屏障消息
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //发送同步消息
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //取消屏障消息
            mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //移除消息
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

5
performTraversals这个方法非常长,有800行左右,我们只关注重要逻辑,有感兴趣的小伙伴可以去细研究。

    void doTraversal() {
            ...
            performTraversals();
            ...
    }
    
    private void performTraversals() {
            ...
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //测量
            ...
            performLayout(lp, mWidth, mHeight);  //布局
            ...
            performDraw();  //绘制
            ...
    }

6

    //ViewRootImpl.java
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
     //ViewRootImpl.java
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
        ...
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
            ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值