View的绘制流程

activity显示页面流程

    对于一个activity启动流程之前已经分析过,那么一个activity页面是怎么绘制出来的呢?如下图:

view 绘制流程.png

    主要流程总结:

  1. ActivityThread中启动了activity,会先调用activity的attach方法。
  2. attach方法中会实例化一个PhoneWindow对象,在PhoneWindow对象中会实例化一个DecorView。
  3. DecorView会根据Activty的主题配置,选择使用哪个系统的布局文件,然后使用layoutInflate来解析文件为view,提取出其中id为ID_ANDROID_CONTENT的控件,作为activity布局文件的父容器。
  4. ActivityThread中调用activity方法onCreate,我们在onCreate中会调用setContentView来设置布局。
  5. setContentView中会使用layoutInflate来解析布局的xml文件为一个view对象,将view对象addView到DecorView的父容器中
  6. ActivityThread调用了activity的onResume方法,方法执行完成后,activity中会设置DecorView先为不可见,然后将该viewaddView到WMS中。
  7. addView时候,会先生成一个ViewRootImpl对象,保存DecorView对象,后续所有view刷新操作都由ViewRootImpl来管理。
  8. viewRootImpl会调用其内部方法requestlayout,来进行view的测量,布局和绘制等流程。
  9. WMS会和surfaceFlinger进行交互,开始绘制界面显示给用户。

    这个流程中,每个activity都会生成一个PhoneWindow对象,该对象实现了Window接口,同时每个PhoneWindow下都会有一个DecorView对象,该对象继承自帧布局。每个DecorView都会被包装到一个ViewRootImpl对象中。APP进程中,只会存在一个Windowmanager代理对象,即WindowManagerGlobal,内部会维护一个WMS代理类。

  • 加载布局时候的参数attachRoot为true还是false的区别:
  1. 如果为true,如下图代码,会将布局对象view插入到root的布局中
  2. 如果为false,那么生成的view对象,只会使用root中的布局参数,但是不会对root有关联,不会插入到root布局中
if (attachToRoot) {
   root.addView(view, params);
} else {
   view.setLayoutParams(params);
}

ViewRootImpl的requestlayout方法流程

view 绘制流程.png

View绘制流程2.png
总结下:

  1. viewRootImpl的requestlayout方法会调用其内部对象DecorView的方法进行绘制。
  2. 首先会调用其measure方法进行测量。
  3. DecorView中会依次调用其子布局的measure方法,分别计算出子布局的大小,最后计算出自己的大小
  4. 接着会调用DecorView的layout方法,计算其布局位置。
  5. 同比DecorView会依次遍历其子布局的layout方法,计算出各自的位置
  6. 最后会调用draw方法,DecorView会调用自己的onDraw方法,接着会通过dispatchView通知子布局调用其onDraw方法进行绘制。
关于MeasureSpec:
  • 组成

    MeasureSpec是一个int值,但是其内部包含了两个信息,一个是size大小,一个是size的模式,其中高两位是模式mode,低30位是size。
为啥要这样呢?

  1. 其实就是为了减少内存,每个view都需要两个MeasureSpec,然后一个activity有好多view,每个MeasureSpec都需要记录mode和size,通过这样放到一个int值中,可以减少内存
  2. 为了使得mode和size可以对应起来
  • 关于MeasureSpec中的mode
  1. 对于布局xml中的设置的width,对应代码中是什么呢?
    xml中确切的数值宽度,代码中对应EXACTLY
    xml中的MATCH_PARENT,对应EXACTLY
    xml中的WRAP_CONTENT,对应AT_MOST
    xml中设置为0,对应UNSPECIFIED
  • 测绘过程中,如何确认子布局大小的呢?(定义父布局大小为size,子布局为 childSize)
  1. 如果子布局是 大于0的具体数值, 那么子布局就会以childSize 作为宽高进行计算,模式为EXACTLY
  2. 如果子布局是 MATCH_PARENT,
         父布局是EXACTLY -> 子布局以size为准计算,模式为 EXACTLY
        父布局是AT_MOST -> 子布局以size为准计算,模式为 AT_MOST
        父布局是 UNSPECIFIED -> 子布局以size为准,模式为 UNSPECIFIED
  3. 如果子布局是 WRAP_CONTENT,
        父布局是 EXACTLY -> 子布局以size为准,模式为AT_MOST
        父布局是 AT_MOST -> 子布局以size为准,模式为AT_MOST
        父布局是 UNSPECIFIED -> 子布局以size为准,模式为 UNSPECIFIED
    除此之外,其他情况都是 size为0,模式为UNSPECIFIED

一些问题

  • 手机锁屏后,viewRootImplp还会发送屏障消息吗?

不会
手机锁屏,会触发activity的onStop方法,对于viewRootImpl的requestLayout方法,会判断如果activity处于stop状态,那么就不会继续往下走了,而发送屏障消息是在requestLayout中先发送的,所以答案是不会

  • requestlayout 和 invalidate的区别
    requestLayout是在用于重新绘制整个view树,确定其大小,布局最后进行绘制。而 invalidate 只是用于重新调用当前view的onDraw方法,重新绘制当前view

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY43FZ8o' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片