聊聊 Android 的 GUI 系统

你长得辣么好看,我想着要更详细地了解你 。今天,让我们一起来聊聊 Android 的 GUI 系统 。
缘起在2019年的 google I/O 大会上,Jetpack 团队首次为大家介绍了 Jetpack Compose,这是一种全新的 Android UI 组件库 。当时演讲者为大家分享了一张图,描述了 Android 10 年里的在 UI 方面简要发展历史,在长达 10 年的发展过程中,Google 针对不同的问题做出了很多的调整,但是唯独在 UI 构建方面,最初的那一套 UI 构建体系一直沿用至今,几乎没有做任何调整 。
 

聊聊 Android 的 GUI 系统

文章插图
 
 
Compose 可以说是 UI 体系的一种颠覆,当然,我今天并不是来推销 Jetpack Compose 的,而是因为我突然间发现,Android 诞生了这么长时间,自己也做了辣么长时间的 UI boy,可是如果你要我立马说出 View 小姐姐是怎么在屏幕上给展示出来的,我竟无语凝噎 。本想着雨露均沾,结果是万花丛中过,片叶不沾身,这怎么能忍!看着UI 小姐姐那真挚的眼神,不给它扒一扒感觉都是一种罪恶 。
目标希望通过这次梳理,能对 Android 整体的 View 框架体系大致流程上能有清晰的认识 。起于 App 层,止于驱动层,并且从中挑一些重要的内容来讲述,方便理清众多对象之间的关系脉络,从而在整体架构上能有比较清晰的认知 。这样,在阅读源码细节时候不至于发出哲学三连问——我是谁?我从哪儿来?要到那儿去?
那么,开搞!
我是 Activity我是一名交际花,专注于于 UI 界面显示和处理,是应用程序中各组件里人气最高的偶像之一 。我在江湖中能有如此地位,那还得多亏了 Android 爸爸对我不吝的包装 。对于开发者来说,只需要简单的调用setContentView、onCreate、onStart 等方法,我就能将他们想要显示的内容展现出来 。很简单是吧,因为我是整个UI体系中离开发者最近的一个窗口了,只有让开发者用起来足够爽了,他们才会喜欢上我啊,所以呢,Android 爸爸也是对我花了很多小心思呢 。我呢,将一系列生命周期相关的回调用模板方法模式的一种设计模式封装,然后暴露给开发者,至于一些那些粗活累活我就汇报给 Android 爸爸去处理,毕竟作为一个 idol ,人设是万万不能倒的 。比如像 setContentView 这种大部分情况下只是传递了一个 xml 的布局的家伙,又要解析 View tree,又要构建的,想想都麻烦,我就很机智的交给 framework 去处理了 。
【聊聊 Android 的 GUI 系统】你别看我多风光的样子,但是本质上,我也只是一个 window 而已啦 。
我是View我是 app 层面向开发者比较核心的 UI 相关类,目前我在源码中的实现接近 3W 行 。我呢还有一个优秀的 child ,名字叫 ViewGroup 。ViewGroup 通过组合模式,而能够在自身内部存在更多的 View 或 ViewGroup,这样一来,从结构上看,我们像是俄罗斯套娃,你中有我,我中有你 。其实除了 View 和 ViewGroup 这些家喻户晓的明星成员外,View 家族中还有 ViewParent 、ViewRootImpl 这些重要的幕后成员,你可千万别以为 ViewParent 就是我的爹地,它虽然叫 ViewParent 但是它就是一隔壁老王,和我一毛钱关系也没有 。虽然我和 ViewParent 清清白白的,但是 ViewGroup 和 ViewRootImpl 都实现了 ViewParent 的接口方法 。
Activity 的setContentView()本质是要将 DoctorView,也就是 View 树的根设置到 ViewRootImpl 中 。ViewRootImpl 发起遍历(调用performTraversals()函数) 后,各个 View 元素就能得到系统的最终“分配结果” 。这个“分配结果”至少会包含两个方的内容:View 对象的尺寸大小和位置,再加上 View 自身的 UI 内容,如此便构成了 UI 显示的基本三要素 。而这重要的三要素,它们在遍历的过程中分别对应以下三个函数:
  • performMeasure 用于计算 View 对象在 UI 界面上的尺寸位置,对应 View 的onMeasure
  • performLayout 用于计算 View 对象在 UI 界面上的绘图位置 。对应 View 的 onLayout
  • performDraw 上述两个属性确定后,View 对象就可以在此基础上绘制 UI 了 。对应 View 的 onDraw
上面这三个函数是在 ViewRootImpl 中展开的,对于开发者来说,我们面对更多的则是 View 与 ViewGroup 以及它们的子类,下面是 View 相关的一些生命周期回调:
  • measure
测量该控件的大小 ,如果是ViewGroup还需测量子控件大小,measureChildren或调用子控件的measure来触发子控件元素的onMeasure方法
  • layout
当View分配所有的子元素的大小和位置时,在onLayout方法被调用之前getWidth(), getHeight()是获取不 到控件的大小


推荐阅读