美文网首页安卓开发Android技术知识Android开发
Android APP启动过程分析(2)——Measure、La

Android APP启动过程分析(2)——Measure、La

作者: thinkChao | 来源:发表于2017-05-05 16:22 被阅读291次

    上一篇文章从ActivityTHread.main()讲到了ViewRootImpl.performTraversals(),这篇则进入performTraversals()的执行流程,也就是大家所熟知的Measure、Layout、Draw三大流程。

    关于三大流程讲解的相关文章太多太多了,之所以自己想写一篇,是因为看别人的文章总是磕磕绊绊,他们的讲解方法、重点各不相同,所以总是需要自己参考多篇文章,再加以自己的梳理才能有个相对完整的理解,这就是我也想写一篇的原因,写一篇一气呵成、脉络清晰的讲解出来。

    可是后来在查找相关博客时,发现了一篇,无论是在讲解思路、讲解形式、讲解重点上,都是我心中想要完成的文章。文章写的非常细致,讲解特别到位,很适合没有接触过这三大流程的同学学习,相信看完自己会在心中有个整体脉络,一定可以看懂的。个人认为,比《Android开发艺术探索》这本书要讲的更容易理解,至少对于初学者来说是的,我是看了这篇文章,再去看书,才明白书中讲的是是什么意思。

    我看过之后,已经没有想要书写的欲望了,所以就把这篇文章的链接放在这,大家如果没有看过这篇文章,而又想学习这三大流程,可以一看。当你看完这篇文章,可以再看看我在下面的总结。
    Android View的绘制流程

    一、Measure##

    我一直不明白这个过程为什么取名为measure,中文翻译成“测量”。一说测量,给我的直接感觉就是:应该先把View给画出来呀,然后我们才能测量这个View的宽和高。可是画是最后一步,这一步的目的是根据用户的布局设置,来确定View的宽和高,而不是我们认为的“测量”,总感觉用这个词不太贴切。所以,这一点有点搞不懂,如果有明白的同学,欢迎在底下留言告诉我。

    Measure过程是这三个里面最复杂的了,过程我就不详解了,在我前面推荐的博客里面讲解的非常到位了。当我们看完之后,虽然对过程明白了,但相信我,用不了多长时间,只要不再接触,肯定会忘记的。所以,我们还是要在脑子里建立一个整体脉络,抛开一些细节,方便日后回忆。在我看来,记住两点就够了:一个是MeasureSpec和LayoutParam,这两个对象贯穿整个measure过程;还有一个就是在我们脑海中,建立一个ViewTree,MeasureSpec在整个ViewTree中是如何传递的,理清整个流程。

    1、MeasureSpec和LayoutParam###

    LayoutParam####

    这个很好理解,LayoutParams就是我们在xml写的时候设置的layout_width和layout_height 转化而来的。而且从它的名称也能看出来,翻译过来就是“布局参数”。

    MeasureSpec####

    记住一个定义

    MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算,得出的一个针对子View的测量要求,这个测量要求就是MeasureSpec。

    记住两张图

    MeasureSpec SpecMode
    三点总结
    1) EXACTLY与AT_MOST的区别就是:只要一个View的SpecMode是EXACTLY的,不论它的子View是match_parent还是wrap_content的,它的size都是固定的;如果一个View的SpecMode是AT_MOST的,那么分两种情况:如果它的子View是match_parent的,那么它的size就是指定的size,如果它的子View是wrap_content的,而且比parent小,那么它的size也会相应的跟随其子View变化。

    2) MeasureSpec是测量要求,而不是测量结果,测量结果的确定需要子View的反馈,为什么?就因为有些View的SpecMode是AT_MOST的,也就是不确定的。如果都是EXACTLY的,那整个ViewTree遍历完,测量结果也就出来了,就不需要再层层反馈回去了。

    3) 定义中说:MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得来的,那顶层View(也就是DecorView)的MeasureSpec是怎么来的?答案是:DecorView 的 MeasureSpec是Windows传过来的,其SpecMode是EXACTLY,size是屏幕的高和宽。

    2、建立View树##

    View树是什么东西?意思就是给我们的View建立一个布局图,而这个图是树形的。View的measure过程就是从根节点开始遍历整个树,MeasureSpec就在遍历的过程中,层层向下传递。前面我们说道,MeasureSpec只是测量要求,而不是测量结果。所以在遍历到叶子节点的时候,还会层层向上传递回来,这个过程就会给出最终的测量结果。

    光说没有图,总是不好理解,而且记不住,所以我下面给出一张树图会更直观一些,这张图是我推荐的那篇博客里面的,我直接拿来展示了,细节大家可以去详细了解。

    假设给出下面一段布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"    
       android:id="@+id/linear"
       android:layout_width="match_parent"    
       android:layout_height="wrap_content"    
       android:layout_marginTop="50dp"    
       android:background="@android:color/holo_blue_dark"    
       android:paddingBottom="70dp"    
       android:orientation="vertical">    
       <TextView        
        android:id="@+id/text"       
        android:layout_width="match_parent"     
        android:layout_height="wrap_content"  
        android:background="@color/material_blue_grey_800"       
        android:text="TextView"        
        android:textColor="@android:color/white"        
        android:textSize="20sp" />    
       <View       
          android:id="@+id/view"       
         android:layout_width="match_parent" 
         android:layout_height="150dp"    
         android:background="@android:color/holo_green_dark" />
    </LinearLayout>
    

    那么整个布局对应的树图是这样的:

    树形布局图

    之所以把这张图摆在这里,一是为了方便理解,其次还有利于记忆,每当我回想measure的整个过程时,我都会想象有个MeasureSpec在这张树中向下传递。

    二、Layout##

    Layout过程相对Measure过程要简单很多,看许多博客的篇幅就能看出来。这里我也不详解具体过程了,我再写一遍也没任何意义,这个过程都被别人写烂了,我还能写出花来不成?更何况我的水平还不如别人……

    但我还是要写几点我的总结,因为看别人写的博客总是晕晕乎乎,大部分都是跟随源码,一步步往下写,自己心中如果没有一个整体的流程和大体的理解的话,很容易看着看着就懵了。

    1、在measure过程中我们知道,是从跟View(也就是一个ViewGroup)向下遍历,直到遍历到叶节点(也就是一个具体的View),测量出其大小,然后向上回溯,逐步确定ViewGroup的大小。也就是先确定子View的大小,然后再确定的父View的大小。那我们就要想了,在layout过程中,是先确定子View的位置,还是父View的位置呢?答案是先确定父View的位置,或者说这个View自己的位置,然后层层向下传递,一一确定子View的位置。这个就牵扯到layout()方法和onLayout()方法了。

    2、在Layout过程当中,频繁出现的两个方法:layout()和onLayout(),一开始就被弄糊涂了,搞不清楚它俩什么关系。后来捋顺了,简答总结来说就是:layout()方法确定View本身的位置,onLayout()则会确定所有子元素的位置。具体是怎么个流程呢?

    3、一个View在layout()方法中完成自己的定位,然后就通过onLayout()方法去调用子View的layout()方法,子View又会通过自己的layout()方法来确定自己的位置,这样一层一层的传递下去就完成了整个View树的定位过程。

    上面的三点总结是我在看这块内容之初,有些困惑我的地方,我也是通过看《Android开发艺术探索》搞明白的,虽然这本书没有把measure过程讲的清晰一些,但这里的问题还是提到了。所以在找参考时,还是尽量多看几个,每个作者写的侧重点都不同,有些疑问这里有解答,有些就在别处。

    三、Draw##

    推荐参考:Android View的绘制流程

    完。

    相关文章

      网友评论

      • 请叫我章鱼哥:这篇文章将的简直棒极了,我在之前重来看不懂,但今天看了你的博文之后,恍然大悟:+1:

      本文标题:Android APP启动过程分析(2)——Measure、La

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