D90cd1a2b08027ad7bcc746d34397b81
重学安卓:百闻不如一见的 视图系统 架构全貌

前言

很高兴见到你!

上一期,我们 全网独家地 以 “所见即所得的 触控体验” 为切入点,从零开始构思了一款视图系统,并自恰地解释了当下安卓视图系统中 某些细节之所以如此设计的缘由,相信阅读完这篇文章的小伙伴,对视图系统背景状况的印象 已跑赢了全网 90%的 移动开发者!😉

比如现在我们确知了:当我们谈论视图系统时,我们实际上是在谈论 在冯诺依曼计算机的背景下的 人机交互系统。因为一旦脱离了这个背景,就构不成 输入输出设备 的存在,也就谈不上对可编程内容的输入和输出,更不要说营造 符合用户直觉的 实时的 输入输出反馈。

所以,关于视图系统,就算遗忘了大部分细节,通过上一篇文章,我们也至少知道了,从宏观来看,它主要就是包含 彼此独立且交替着的 输入和输出 这两大过程 —— 在此之前,如果我们对此不假思索、和普通用户一样去 感知(觉得 视图系统 “像光一样 具有波粒二象性”)、而不是 思考(知道所谓的 “实时响应” 只是计算机速度过快给人的错觉、实际上每一段触控都包含了 无数次 顺序执行的 输入 - 输出 - 输入 - 输出 ...),那么我们便无法真正理解和驾驭 视图系统 为我们做功。

文章目录一览

  • 前言
  • 为什么要反思 视图系统
  • 作为差强人意的 安卓视图系统
  • 视图系统 为什么要基于 C/S 架构
  • 一睹 视图接口 和 视图服务 的抽象
  • 当下 安卓视图系统 的怪状
  • 综上

为什么要反思 视图系统

原因很简单,做客户端开发的,100% 要和视图系统打交道。

尽管许多技术层面的问题,从产品层面来看完全不是问题,

(比如 《你 hold 不住自定义视图,只因缺了这把钥匙》 一文提到的,尽量用普适的、现成的交互方案,这样用户的学习成本最小,不到万不得已的时候 不要自己去设计自定义视图,否则费力不讨好。
又比如 Android 4.4 ~ 5.1 各种不成熟、各种毛病需要兼容,但从产品角度来看,上架的 App 完全可以考虑 6.0 起步,因为 2019 年还在用 4.4 系统的用户,大概率不会是正经用户 —— 从某位独立开发者分享的数据统计来看,Google Play 上的 4.4 用户 “贡献了” 78% 的差评和近乎为 0 的增值付费)

然而唯有 正确地理解 一个系统 所服务的普适需求 乃至 推知 和 确立 该系统的必要构成,才能在关键时刻有条不紊地 界定问题的边界、判定事物的归属(比如你知道了 LayoutInflater 是视图排版的 前置辅助工具,那么在谈论具体的排版流程时,你便不会将其考虑进去,因为你明确地知道 它的边界、它的归属),从而 不做无用功地、毫不费力地迎刃而解 —— 你一定不希望 老板交代你任务时 一脸懵逼、不知从何下手,对吧。

作为差强人意的 安卓视图系统

所以 本来我是打算趁热打铁、接着上一篇 打下的认知基础,来继续深入对 排版过程的反思,

然而在我调研了一圈后发现,网上的文章多是 千篇一律地 贴代码、告诉你 how、how、how,就是不告诉你 why。由此引发了我 “走在路上,脑壳疼” 的难受。

比如,你知道 为什么要 独立地存在 “测量” 的流程吗?是否能够提供案例,来证实这个过程的存在 确实是 某种场景下解决某种问题时的 不可替代?否则视图系统 为什么要存在这样的设计?

通过 LayoutParams 我能拿到对宽高的描述,那么我直接在 “布局” 环节根据描述来计算视图的 Left、Top、Right、Bottom、Height、Width 不也可以?(如我所料,Flutter 下就没有专门的测量流程)

就是这样烧脑的、但在我看来是不可绕过的关键,消耗了我大量的时间来思考和反复论证。😵

所以前几天有位读者私下感叹说,要是互联网上的每篇文章都能像 《这样理解,你也能在 30 秒内讲明白 TCP 三次握手》 一样,能用 "你听得见吗" "我听得见" "我也听得见" 让人秒懂三次握手的本质 该有多好,

实际上在我看来,并不是撰文、教书的人 讲解方式 的问题,而是 设计系统 和 撰写文档 的 通常不是同一个人。

我们常常能在源码中看到精妙的设计,文档却常常只顾 how 而忽略了对 作为重中之重的 如此设计的用意 的介绍。

也即,上游的设计师 出于各种原因 没有出面解释,导致中游的教师或作者 需要花费大量的精力 才有一丝丝的机会正确地理解到。

并且,从生物代谢的角度来说,照本宣科、人云亦云,张口闭口 ACK、SYN(三次握手中的术语),显然是最节能的,尽管这样一来他们自己都蒙在鼓里、不知道自己究竟在讲些什么。

TQL、AWSL

于是,在 “现状如此” 的背景下,我决定将本篇文章分两部分讲,上半部分着重于反思各平台通用的 视图系统架构实现 的全貌,下半部分将对 安卓中的局部实现 作有选择地介绍。

在之前的 《你 hold 不住自定义视图,只因缺了这把钥匙》 一文中已提到过:源码的实现 绝非一蹴而就,它是几十年下来不断演化、不断变更的结果,所以我们务必 首先基于反思 来为自己准备一把切入的钥匙,然后在这把钥匙的指引下 有目的地、点到为止地 到源码中去确认,而不是打一开始就盲目地一头栽进去 试图事无巨细地 全盘通吃。

好了 不多说了,下面让我们跟随 深度思考的脚步,从抽象到具体地 将视图系统架构实现的全貌 无痛地过一遍!

视图系统 为什么要基于 C/S 架构

无论是 Android、iOS、还是 MacOS、Windows、Linux,视图系统为了实现 输入和输出的完美协调,必然需要基于 C/S 架构。

为什么呢?

首先,排版结果的渲染 和 屏幕事件的接收,是通用的、且 使用频率极高,所以它应当在处于 支持高效率运行的、规避数据被篡改或破坏 的环境下运作,

根据大学时期我们学过的《操作系统》课程,我们能够得知,进程通常存在两大类:一类是用户进程,一类是系统进程。显然 独立存在 的系统进程 能够为上述需求提供理想的环境:运行时无须申请任何权限、运行效率更高、且规避了内部数据惨遭外部篡改 而导致的运行结果不可预期的风险

由此我们的视图系统 便不得不 分为两部分 来运行,一部分是上述提到的,常驻 在系统进程中的 视图服务,另一部分就是 运行在 用户自己开启的用户进程中的 视图接口(客户端),客户端的 排版结果 和 事件分发,都是通过这个“接口”去向 视图服务 传递和接收的。

除了上述提到的 运行时高效、运行时安全,一分为二的设计的另一个好处在于 实现了效率和节省资源的平衡:让频繁使用的底层服务 常驻、让随机使用的客户端 随叫随到、用完即走(关掉 App 的进程,进程申请的这部分资源 也就归还给系统了)。

所以 至此我们确立了:视图系统需要 “一分为二” 的设计 ——

  • 让使用频率极高的视图服务 常驻系统进程,以减少频繁创建进程导致的开销,以及由于是在系统进程中运行,因而效率更高、更安全。

  • 与此同时,让客户端 在用户进程中生死存亡,从而达到即用即走、节省系统资源的目的。

top Created with Sketch.