B9ed19586a14b7ebb883c08134188933
重学安卓:是让人 过目难忘 的 Android GUI 族谱解析啊!

温馨提示:如果这是第一次接触《重学安卓》,可借助 这份在 GitBook 上维护的 “导读” 来快速了解《重学安卓》专栏、获取它的目录、试读内容,以及了解它的最新动态 和 发展状况。

截至目前,专栏已对 体系化文章 做了 730 余次修订,数十位群友告诉我 受专栏的启发 他们也开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创的开源库 和 提供内推机会,订阅后可随时进群交流。

前言

很高兴见到你!

前两期 我们通过 深度思考的方式,分别介绍了 视图系统 的设计依据 和架构思想,相信阅读过前两期的小伙伴,对 泛客户端开发 普遍适用的 视图系统的运作机制 已形成了不可磨灭的印象。😉

Android GUI 系统仍在困扰着 80% 的进阶者

所以本来是打算,继续写上几期 使用场景十分普遍的 通用必用控件组件,没想到,有不少读者 (包括 刚要进阶 或是从 iOS、嵌入式、前端、后端等 技术栈 转 Android 的小伙伴) 私下提到了他们对 Android GUI 系统深入学习的忧虑。

是网上关于 Android GUI 的知识不够多吗?不是的,恰恰相反,网上的知识多如牛毛,甚至还有像上一期《百闻不如一见的 视图系统 架构全貌》文末介绍的 图文并茂的 绝佳的 学习视图的教程。

那为什么还是 让无数刚刚想要进阶 的小伙伴 感到困扰呢?—— 其实最主要的原因是,绝大多数网文 只关心 是什么、怎么做,却对 为什么绝口不提。😦

这使得读者们 首先就没有机会搞明白:为什么要阅读这些文章、为什么要学习这些技术点、每个技术的作用边界 到底是从哪到哪、技术之间的 关系和顺序 又该如何串起来 … 从而网上的教程 再好再多,也无从下手、无从看起。

与此同时,过往的文章中 我们多次提到,凡事唯有 首先通过深度思考 为自己打造一把 单刀直入的魔法钥匙,才有机会打开 异世界的大门,从而借助前人已铺设好的 "是什么、怎么做",来玩转那个领域的技术。

于是,这一期,我们将继续 以深度思考的方式,来 "有层次感" 地铺设关于 Android GUI 系统的 感性认知

考虑到这样 精心打磨的摆渡文 是看一篇少一篇,因此 就算不去 hold 住面试官,也请务必跟随本文的脚步,将 Android GUI 系统的来龙去脉 无痛地过一遍

文章目录一览

  • 前言
  • Android GUI 系统仍在困扰着 80% 的进阶者
  • 是一分为二的理解方式
    • 谁才是可视化排版的根基?
    • View 和 Drawable 不过是排版的模板
    • Layout 和 Inflater 不过是后来者
    • include,merge,ViewStub 是解药的解药
  • 作为承上启下的小结
    • 上文的 "Window" 为何一直加个双引号呢?
    • ViewRootImpl 是怎么帮 Canvas 与窗口对应上的?
    • 是对症下药的 排版渲染 性能优化指南
    • 所以 PhoneWindow 又是什么鬼?
  • 综上

是一分为二的理解方式

阅读过 上一期 的小伙伴已确知:

视图系统主要包括 排版 渲染事件 分发 这两大类工作。

其中 客户端 运行在用户进程中,负责 可视化内容的排版 和 向服务端输出,以及 接收来自服务端的事件 并在视图树中分发

而 视图服务 运行在系统进程中,负责 对排版内容的渲染,以及 传递触控事件给客户端

考虑到 日常工作 和 进阶的困扰 主要集中在 客户端 排版 API 这一块,因而本文主要分为两个部分来讲解:

第一部分主要是 单刀直入地 推理和解析 排版的根基之所在,以及 层层递进的 客户端 排版 API 关系网

第二部分 我们再来全面地 解析整个 Android GUI 系统。

谁才是可视化排版的根基?

早在《重学安卓:Activity 的快乐你不懂!》一文中,我们就对 Android 的可视化系统 做了一次简要的推理,

其实光是截取 这篇文章的中间部分 加以放大,就能 单刀直入地解决 80% 小伙伴 关于排版 的困扰 ——

我们不如从这篇文章的 “Window” 一节开始深入:

“Window” 的存在,主要是为了管理 UI 内容的排版。

那么有没有读者 细思过这句话 —— 它作为管理者,其背后,究竟是 哪位 绘制者 在被管理呢

"那时候" 并没有所谓的 View …… 那么究竟是谁在绘制?…

一路追溯,哦!原来是 Canvas ——

Canvas 才是可视化排版的根基!排版依赖于绘制 —— 在排版这件事上,Canvas 可以没有 View,但 View 不能没有 Canvas。

图片来自网络。图中用户通过 Canvas 手绘了 GUI 界面

Paint 和 Path 都是 Canvas 的小弟,真正 与排版输出 存在直接关系的 最源头 API,就是 Canvas 本 s。

View 和 Drawable 不过是排版的模板

然而 既然是视图系统,那就 不单单是 不规则的绘制本身,

因而在有了 “Window” 和 Canvas 的基础上,还需确立 View / ViewGroup 这样规则的 排版标准,从而我们得以 在这套 可拓展的标准 的基础上,构建 可复用的具体标准(模板)

例如 Button、TextView、ImageView,这些,都是 "可复用的模板",开发者日常只需与 上层的这些 View 打交道,而无需直接与 Canvas 打交道、无需做什么都得先手动基于 Canvas 来写个几百行的排版代码。

并且,如果对 现成的模板感到不满意,也可以自己再封装一个 新的模板,也即人们通常所说的 "自定义控件"。

与此同时,View 只是大致地描述了 视图构建的模板,如果要更改 具体 View 的可视化细节,那岂不是又要接触 Canvas?

因此,同样是出于 灵活性和复用 的考虑,而衍生出 Drawable 的设计,它的存在是用于负责更具体的 可视化细节的模板:

例如通过 ShapeDrawable 来描述 视图的轮廓和背景、通过 StateListDrawable 来描述 视图的点击效果 等等,

8、16、24dp 圆角 16dp 圆角 12、24dp 圆角

图片来自合作方设计的《Fairytales》:是无处不在的 各种规格的 圆角。

使得我们绝大部分情况下 都能使用成熟的 Drawable 模板 来描述排版细节,避免良莠不齐的开发人员 因直接与 Canvas 打交道 而埋下的 不可预知的隐患。

Layout 和 Inflater 不过是后来者

所以 Layout 和 LayoutInflater 不过是在有了 View 和 Drawable 的基础上,才有的后来者呀。

说来你可能不信,可这也许真是 Google 宠 Android 开发者的证明 ~

有好多 iOS 开发者 (包括合作方的老板) 都抱怨,在 iOS 上写布局 实在酸爽,没有预览,全靠想象!

是的,没错,Layout 对应的是 视图树,shape、selector 等 对应的是 Drawable —— Google 模仿微软 Visual Studio 的设计,巧妙地通过 XML 的方式,来实现 一目了然的 声明式编程可实时预览的布局,以及 向 MVC 模式致敬 的 视图 和 视图控制器分离 —— 这些都极大地方便了开发者 创建和修改可视化内容。

然而,XML 声明式编程 在解决上述问题的同时,引入了新的问题:

1.XML 排版资源的复用率极低。例如,当项目中的控件 对圆角 shape 有新的规格要求时,哪怕不同规格之间仅仅是 圆角 dp 存在细微差异,也不能像动态代码那样 通过修改参数即可完成,而是需要 重新创建一个新的 shape Drawable 文件。

2.XML 的解析由 LayoutInflater 负责,LayoutInflater 是通过 深度优先遍历 算法解析 XML 来 构建视图树,从而当布局嵌套层次加深等原因导致的 View 个数过多时,XML 的解析就会愈加耗时。

那怎么办呢?

include,merge,ViewStub 是解药的解药

所以 XML 布局 只是一种 充分非必要 的 构建视图树的方式啊!

我们看到许多 东拼西凑 的性能优化网文,不分场合地兜售 include,merge,ViewStub,然而事实上 它们仅仅是用于 XML 布局的情况 —— 通过减少层级来 解决 inflate 耗时问题

Telegram 源码 中,这种 全动态编码 的布局构建方式,就完全用不上 从 LayoutInflater 到 ViewStub 的这些技术。

作为突如其来的 综上

所以到此为止,对 "Window" & Canvas;View & Drawable;Layout & Inflater;merge & ViewStub 等 技术点 层层递进的关系,是不是 轻轻松松地做到 心中有数 了呢?

所以 不管是 Button、TextView、ImageView,还是后来的 MaterialButton、TextInputLayout 等等,无论其表面如何千变万化,都不过是 为了在特定场景下 符合用户直觉 而衍生出的 新的 具体的 排版模板,本质上都是 基于 Canvas 去绘制 特定样式的 可视化内容

而 Inflate 也不过是发生在 当开发者选择通过 声明式编程 而非 动态编码 的方式 去构建视图树。所以若非使用 LayoutInflater,开发者甚至无须知道 include,merge,ViewStub 的存在。

top Created with Sketch.