双向绑定原理,Android-View的工作原理

ViewRoot和DecorView

1、ViewRoot是什么?

①.ViewRoot对应于ViewRootImpl类

②.是连接WindowManager和DecorView的纽带

③.发起并完成View的三大流程(测量、布局、绘制)

④.ViewRoot需要和DecorView建立联系

[想获取更多相关Android资料群653583088。本群提供免费的学习指导以及免费的解答不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导进群修改群备注:开发年限-地区-经验方便解答问题]

2、ViewRoot如何完成View的三大流程?

双向绑定原理,Android-View的工作原理
WechatIMG125.jpeg
①ViewRoot的performTraversals()开始View的绘制流程,依次调用performMeasure()、performLayout()和performDraw()

②performMeasure()最终执行父容器的measure()方法,并依此执行所有子View的measure方法。

③performLayout()和performDraw()同理

3、View三大流程的作用

①measure决定了View的宽/高,测量后可以通过getMeasuredWidth/Height来获得View测量后的宽/高,除特殊情况外该值等于View最终的宽/高

②layout决定了View的顶点坐标以及实际View的宽/高:完成后可以通过getTop/Bottom/Left/Right获取顶点坐标,并通过getWidth/Height()获得View的最终宽/高

③draw决定了View的显示,最终将View显示出来
MeasuredWidth/height != getWidth/Height()的场景:更改View的布局参数并进行重新布局后,就会导致测量 != 实际值

4、DecorView的作用

①DecorView是*View,本质就是一个FrameLayout

②包含了两个部分,标题栏和内容栏

③内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中

④获取content:ViewGroup content = findViewById(R.android.id.content)

⑤获取设置的View:content.getChidlAt(0)

5、ViewRootIml如何和DecorView建立联系

①Activity对象在ActivityThread中创建完毕后,会将DecorView添加到Window中

②同时会创建ViewRootImpl,调用ViewRoot的setView方法将ViewRootImpl和DevorView建立关联
root = new ViewRootImpl(view.getContext(), display)
root.setView(view, wparams, panelParentView)

6、ViewRoot为什么要和DecorView建立关联

DecorView等View的三大流程需要通过ViewRoot完成

MeasureSpec

1、MeasureSpec是什么?
①MeasureSpec是一种“测量规则”或者“测量说明书”,决定了View的测量过程

②View的MeasureSpec会根据自身的LayoutParamse和父容器的MeasureSpec生成。

③最终根据View的MeasureSpec测量出View的宽/高(测量时数据并非最终宽高)
2、MeasureSpec要点解析
①MeasureSpec代表一个32位int值,高2位是SpecMode,低30位是SpecSize

②SpecMode是指测量模式

③SpecSize是指在某种测量模式下的大小

④类MesaureSpec提供了用于SpecMode和SpecSize打包和解包的方法
3、测量模式SpecMode的类型
①UNSPECIFIED:父容器不对View有任何限制,一般用于系统内部

②EXACTLY:精准模式,View的最终大小就是SpecSize指定的值(对应于LayoutParams的match_parent和具体的数值)

③AT_MOST:最大值模式,大小不能大于父容器指定的值SpecSize(对应于wrap_content)
4、MeasureSpec和LayoutParams的对应关系
①View的MeasureSpec是需要通过自身的LayoutParams和父容器的MeasureSpec一起才能决定

②DecorView(*View)是例外,其本身MeasureSpec由窗口尺寸和自身LayoutParams共同决定

③MeasureSpec一旦确定,onMeasure中就可以确定View的测量宽/高
5、普通View的Measure的创建规则
①View本身布局参数为具体dp/px数值,模式:EXACTLY,尺寸:自身尺寸(不管父容器的MeasureSpec)

②View为match_parent, 模式:EXACTLY/AT_MOST由父容器MeasureSpec决定,尺寸:父容器目前可用大小

③View为wrap_content,模式:AT_MOST,尺寸:父容器可用尺寸(不能超过该尺寸)

④当父容器为UNSPECIFIED时,View为具体数值时规则不变;其余match_parent/wrap_content,模式均为:UNSPECIFIED,尺寸:0

⑤UNSPECIFIED一般用于系统内部多次measure的情况,不需要关注该模式
双向绑定原理,Android-View的工作原理
WechatIMG126.jpeg

View的工作流程

①measure:测量——确定View的测量宽/高

②layout:布局——确定View的最终宽/高和四个顶点的位置

③draw:绘制——将View绘制到屏幕上
1、Measure 过程
1、View的measure过程及要点
①View的measure方法是final类型方法——表明该方法无法被重载

②View的measure方法会调用onMeasure方法,onMeasure会调用setMeasuredDimension方法设置View宽/高的测量值
2、View的onMeasure源码要点

        //1. setMeasuredDimension方法设置View宽/高的测量值
        setMeasuredDimension(
                //2. 第一个参数是获得的测量宽/高(通过getDefaultSize获取)
                getDefaultSize(getSuggestedMinimumWidth(),  //3. 获取的建议最小的宽/高
                                    widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(),
                                    heightMeasureSpec));
}

1.setMeasuredDimension方法设置View宽/高的测量值(测量值通过getDefaultSize获取)

2.getDefaultSize用于获取View的测量宽/高

3、View的getDefaultSize源码要点(决定了View宽高的测量值)

  public static int getDefaultSize(int size, int measureSpec) {
      int result = size;
      int specMode = MeasureSpec.getMode(measureSpec);
      int specSize = MeasureSpec.getSize(measureSpec);

      switch (specMode) {
      //2. UNSPECIFIED模式时,宽/高为第一个参数也就是getSuggestedMinimumWidth()获取的建议最小值
      case MeasureSpec.UNSPECIFIED:
          result = size;
          break;
      //3. AT_MOST(wrap_content)和EXACTLY(match_parent/具体值dp等)这两个模式下,View宽高的测量值为当前View的MeasureSpec(测量规格)中指定的尺寸specsize
      case MeasureSpec.AT_MOST:
      case MeasureSpec.EXACTLY:
          result = specSize;
          break;
      }
      return result;
  }

4、View的getSuggestedMinimumWidth/Height()源码要点

protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

1.如果View没有背景,View的最小宽度就为android:minWidth这个参数指定的值(mMinWidth),没有指定则默认为0

2.如果View有背景,会从mMinWidth和背景的最小宽度中取最大值。

3.背景的最小宽度(getMinimumWidth())本质就是Drawable的原始宽度(ShapeDrawable无原始宽度,BitmapDrawable有原始宽度——图片的尺寸)
5、View的wrap_content和match_parent效果一致的原因分析
①根据View的onMeasure方法中的getDefaultSize方法,我们可以发现在两种模式下,View的测量值等于该View的测量规格MeasureSpec中的尺寸。

②View的MeasureSpec本质是由自身的LayoutParams和父容器的MeasureSpec决定的。

③当View为wrap_content时,该View的模式为AT_MOST,且尺寸specSize为父容器的剩余空间大小。

④当View为match_parent时,该View的模式跟随父容器的模式(AT_MOST/EXACTLY), 且尺寸specSize为父容器的剩余空间大小。

⑤因此getDefaultSize中无论View是哪种模式,最终测量宽/高均等于尺寸specSize,因此两种属性效果是完全一样的(View的大小充满了父容器的剩余空间)

⑥除非给定View固定的宽/高,View的specSize才会等于该固定值。
6、自定义View需要重写onMeasure方法,并写明两种模式的处理方法

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);

        if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
            //2. 均为wrap_content时, 将值设置为android:minWidth/Height属性指定的值
            setMeasuredDimension(mWidth, mHeight);
        }else if(widthSpecMode == MeasureSpec.AT_MOST){
            //3. 哪个为wrap_content哪个就用android:minXXX属性给定的最小值
            setMeasuredDimension(mWidth, heightSpecSize);
        }else if(heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize, mHeight);
        }
    }

7、ViewGroup(抽象类)的measure流程
①ViewGroup没有onMeasure方法,只定义了measureChildren方法(onMeasure根据不同布局难以统一)

②measureChildren中遍历所有子元素并调用measureChild方法

③measureChild方法中会获取子View的MeasureSpec,然后调用子元素View的measure方法进行测量

最后说一下我的学习路线

其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:

  1. 架构师筑基必备技能
  2. Android框架体系架构(高级UI+FrameWork源码)
  3. 360°Androidapp全方位性能调优
  4. 设计思想解读开源框架
  5. NDK模块开发
  6. 移动架构师专题项目实战环节
  7. 移动架构师不可不学习微信小程序
  8. 混合开发的flutter

双向绑定原理,Android-View的工作原理

Android学习的资料

我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。需要的小伙伴可以私信我【进阶】我免费分享给大家,或者直接点击下面链接领取,谢谢大家这么久以来的支持。

Android学习PDF+架构视频+面试文档+源码笔记

如果你有其他需要的话,也可以在GitHub上查看,下面的资料也会陆续上传到Github

330页PDF Android学习核心笔记(内含上面8大板块)

双向绑定原理,Android-View的工作原理

Android学习的系统对应视频

双向绑定原理,Android-View的工作原理

总结

我希望通过我自己的学习方法来帮助大家去提升技术:

  • 1、多看书、看源码和做项目,平时多种总结

  • 2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理

  • 3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习

  • 4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!

    进阶学习资料领取方式:GitHub

进阶学习资料领取方式:GitHub*

希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!

上一篇:手写 Vuex 4.x


下一篇:winform 使用task不卡界面的2种方式