5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释)

本篇主要面对有一定Android基础的同学,但是Android初学者的话,也不要急。在网上可以看到很多文章,会告诉你onMeaure是什么?里面的参数是什么意思?会教你怎么获取MeaSpec的size和mode(Unspecified、Exactly、AtMost)。也会告诉你怎么重写 onLayout,怎么使用layout方法去布局子元素,怎么实现流式布局,自动换行。还有会告诉你重写onDraw(canvas),去玩canvas去绘图,然后你会了解到PathMeasure、Bitmap、Drawable等。

但是我们还是很懵逼,为啥重写这些方法之后就行了呢?ViewGroup的measure和View的measure有啥区别呢?为啥我们有这些困惑?其实是我们的学习路线有点问题。

最好的学习方式都是top-bottom,自顶向下的,你考虑下如果我直接告诉你,onMeasure就是测量函数,里面的Mespec参数就是要通过getMode方法取模式,你是不是很懵逼、很排斥。但是如果我告诉你其实整个Activity视图元素都是在一个窗口里面的(PhoneWindow),窗口有个根View(DecorView),它的展示过程有测量、布局、绘制3个操作(measure, layout, draw),他会遍历他下面所有的View和ViewGroup,ViewGroup又遍历ViewGroup。如果把DecorView看成树的根节点的话,绘制过程就是一个树的深度遍历过程。你想要操作View(控制大小,控制布局,控制样式),其实就是操作onMeasure、onLayout、onDraw。这样讲解是不是就能懂了呀 呀呀!!!


前言

在上篇5分钟告诉你,Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)分析Android源码是怎么调用Activity的生命周期中,我们提到了ViewRootImpl的创建,然后通过它的performTraversal,分别执行了perfromMeasure、performLayout、performDraw,来进行遍历所有View树中View节点。

这篇我先提出几个问题,然后咱们带着问题来找答案。

  1. ViewRootImpl是什么,它是什么时候创建的?
  2. DecorView是什么?ViewRootImpl是怎样执行遍历View的过程的?
  3. onMeasure、onLayout、onDraw是什么时候调用的?他们的参数是谁给他们的?

篇幅比较长,通过几个问题查看源码来解读的,请耐心的看。想先大体了解一下的同学,可以直接滑到最后结论。

【转载请注明出处:5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释) CSDN 王智博


正片

1.初识DecorView

上篇我们讲解Activity生命周期的时候讲解到,Activity的创建和onCreate的回调是在ActivityThread.performLauchActivity,Activity创建之后,调用attach方法

step1. activity.attach(appContext, this, getInstrumentation(), r.token,...)

activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

我们进去看看attach的实现,可以看到实例了一个PhoneWindow

        //创建PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
      
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        mWindowManager = mWindow.getWindowManager();

之后是调用Instrument.callActivityOnCreate,然后调用Activity的onCreate和setContentView,我们来看看setContentView

step2. mInstrumentation.callActivityOnCreate(activity, r.state)

mInstrumentation.callActivityOnCreate(activity, r.state);

看到调用的是Window的setContentView方法,这里的window是phonewindow,我们看下phoneWindow的setContentView实现. 

step3. Activity.setContentView(LayoutRes int layoutResID)

    public void setContentView(@LayoutRes int layoutResID) {
       //WINDOW的setContentView
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到 如果没有根布局会installDecor,之后调用LayoutInflater的inflate(layoutResID)方法。

step4. PhoneWindow.setContentView(LayoutRes int layoutResID)

public void setContentView(int layoutResID) {
        //如果没有根View,就安装DecorView
        if (mContentParent == null) {
            installDecor();
        } 
        ...
        //把layout布局文件解析出来,addView到根View
        mLayoutInflater.inflate(layoutResID, mContentParent);
        
    }

我们看下installDecor方法,调用generateDecor(new 一个DecorView)。那么我们再看看DecorView是啥?

step5. PhoneWindow.installDecor()

private void installDecor() {
        if (mDecor == null) {
            //生成Decor
            mDecor = generateDecor(-1);
            mDecor.setIsRootNamespace(true);
        } else {
            //设置window
            mDecor.setWindow(this);
        }
}

小结:可以看到DecorView就是一个FrameLayout,他是在setContentView里面初始化的。

class DecorView extends FrameLayout

2.ViewRootImpl是什么?什么时候初始化的?

上篇中我们介绍了,ActivityStack在通知ActivityThread LauchActivity之后,之后会通知ActivityThread handleResumeActivity,本篇文章就不详细解读了,不明白的同学可以看上篇。我们可以看到先执行performResumeActivity通知Activity去onCreate和onResume,之后调用wm.addView(decor, l),把decorView装载给wm。

step6. ActivityThread.handleResumeActivity(IBinder token ...)

void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ...
        r = performResumeActivity(token, clearHide, reason);

            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                ...
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        //很重要。。  
                        wm.addView(decor, l);
                    }
}

那么wm是什么呢?

wm是getWindowManager()获取的,getWindowManager()会最终调用PhoneWindow的setWindowManager。

step7.  Window.setWindowManager(WindowManager wm, IBinder appToken, ...)

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ...
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

可以看到mWindowManager 是createLocalWindowManager(this)返回的,那我们看下createLocalWindowManager(this)方法。

step8.  WindowManagerImpl.setWindowManager(WindowManager wm, IBinder appToken, ...)

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

看到了实际上wm是WindowManagerImpl对象。那step6中的wm是WindowManagerImpl类型,我们来看下addView方法。

step9.  WindowManagerImpl.addView(View view, @NonNull ViewGroup.LayoutParams params)

 public void addView(View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

看到实际是调用mGlobal的addView方法。

step9.  WindowManagerGlobal.addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow)

从6行可以看到, WindowManagerGlobal.add方法,创建了一个viewRootImpl,所以viewRootImpl的创建是在AMS通知ActivityThread的handleResumeActivity里面创建的,再通俗点就是Activity onResume调用之后。

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            ViewRootImpl root;
       
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);

            mRoots.add(root);
            mParams.add(wparams);
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
               ...
            }
        }
    }

小结:ViewRootImpl是DecorView的操作类,比如measure、layout、draw、requestLayout、invalidate等,它是在handleResume操作中,WindowManagerGlobal.addView 中初始化的。


3.onMeasure、onLayout、onDraw是什么时候调用的?他们的参数是谁给他们的?

从上面第2点我们最后看到addView()方法里面,生成了ViewRootImpl,然后调用了ViewRootImpl.setView(view,wparams, panelParentView),可以看到是把 DecorView 传递进去了,我们看下setView方法.

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...省略

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

               ...省略
            }
        }
    }

我把不重要的都省略了,可以看到第10行调用了 requestLayout(),我们看下英文解释。在添加到窗口管理器之前安排第一次布局,确保我们在接受系统事件的时候在重新布局。所以我们说明这个 requestLayout() 方法是绘制机制的起点,我们从这开始看。

 

 

 

 

总结

1. RequestLayout的过程:View类requestLayout() -> 父类的requestLayout()并且重置缓存标志 —ViewRootImpl类requestLayout()—scheduleTraversals()—doTraversal()—performTraversals();

2:performTraversals()方法做了三件事:1)调用performMeasure();2)调用performLayout();3)调用performDraw();

Measure过程:ViewRootImpl的performMeasure()—View的measure()—具体实现类的onMeasure();
layout过程:ViewRootImpl的performLayout()—View的layout()—具体子类的onLayout();
draw过程:ViewRootImpl的performDraw()—ViewRootImpl的drawSoftware()—View的draw()—具体实现类的onDraw(),ViewGrop的dispatchDraw();

 

完整Android学习路径 请戳我的Android学习之旅(持续更新中...)

从源码角度分析Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)

基于AIDL的 Activity、Service跨进程观察者模式实现与源码解读

走进源码,Android面试最常见Handler、Looper、Message问题总结与解答

Android面试---ListView原理及fling分析

5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释)

参考

从ViewRootImpl类分析View绘制的流程

View的三次measure,两次layout和一次draw

理清Activity、View及Window之间关系

View的绘制过程

Android9.0 Activity启动原理差异解析

发布了52 篇原创文章 · 获赞 58 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览