往期回顾,专栏目录,更新动态,优惠政策,版权须知
温馨提示:如这是第一次接触《重学安卓》,可通过上述链接快速了解《重学安卓》专栏,获取 专栏目录、试读内容、更新动态 和 发展状况。
截至目前,专栏已对 体系化文章 实施 3310 余次修订,数十位群友告诉我,受专栏启发 亦开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创开源库 和 提供内推机会,订阅后可随时进群交流。
前言
上一期《不如我们 从零开始设计一套 视图系统》,我们另辟蹊径以 “所见即所得 触控体验” 为切入点,从零开始构思一款视图系统,并自恰解释当下 Android 视图系统中某些细节设计缘由,相信阅读完这篇文章小伙伴,对 View 坐标体系等机制有了新理解。
例如现在我们确知:当谈论视图系统时,实际是在谈论 冯诺依曼计算机背景下 人机交互系统。因为一旦脱离该背景,就构不成 “输入输出设备” 的存在,也就谈不上对可编程内容的输入和输出,更不要说营造 “符合用户直觉” 的实时输入输出反馈。
所以,关于视图系统,就算遗忘大部分细节,通过上一篇文章,我们也至少确知,从宏观来看,它主要即是包含 “彼此独立且交替着的” 输入和输出这两大过程 —— 在此之前,如我们对此不假思索、和普通用户一样去 感知(觉得 视图系统 “像光一样具有波粒二象性”)、而非 深度思考(知道所谓 “实时响应” 只是计算机速度过快给人错觉、实际上每一段触控都包含 无数次 顺序执行的 输入 - 输出 - 输入 - 输出 ...),那么我们便无法真正理解和驾驭 “视图系统” 为我们做功。
文章目录一览
- 前言
- 为何要反思 视图系统
- 差强人意 Android 视图系统
- 视图系统 为何基于 C/S 架构
- 一睹 “视图接口” 和 “视图服务” 抽象
- 当下 Android 视图系统 怪状
- 综上
- Note 2020.1.29 加餐:
- 过目难忘 Android GUI 关系梳理
为何要反思 视图系统
原因很简单,做客户端开发,100% 要和视图系统打交道。
尽管许多技术层面问题,从产品层面看完全不是问题,
比如 《自定义 View 导读》 一文提到的,尽量用普适、现成的交互方案,这样用户学习成本最小,不到万不得已时,不要自己设计自定义视图,否则费力不讨好。
又如 Android 4.4 ~ 5.1 各种不成熟、亟待适配兼容,但从产品角度看,上架 App 完全可考虑 6.0 起步,因为 2019 年还在用 4.4 系统用户,大概率非正经用户
—— 从某位独立开发者分享统计数据来看,Google Play 4.4 用户 “贡献” 78% 差评和近乎为 0 增值付费。
然而唯有正确理解一个系统所服务普适需求 乃至 推知 和 确立 该系统必要构成,才能在关键时刻有条不紊 界定问题边界、判定事物归属(比如你确知 LayoutInflater 是视图排版 前置辅助工具,那么在谈论具体排版流程时,便不会将其纳入考虑,因为你确知它边界和归属),从而 不做无用功、毫不费力迎刃而解
—— 你一定不希望 老板交代你任务时 一脸懵逼、不知从何下手,对吧。
差强人意 Android 视图系统
所以本来我是打算趁热打铁、接着上一篇打下的认知基础,继续深入对排版过程反思,
然而在我调研一圈后发现,网上文章多是千篇一律贴代码、告诉你 how、how、how,就是不告诉你 why。由此引发我 “走在路上,脑壳疼” 难受。
例如,你可知 “测量” 流程为何 独立存在?是否能提供案例,来证实 “该过程的存在” 确实是 “某场景下解决某种问题” 时的不可替代?否则视图系统为何存在如此设计?
通过 LayoutParams 我可拿到宽高描述,那么我直接在 “布局” 环节,根据描述来计算视图 Left、Top、Right、Bottom、Height、Width 不也可以?(如我所料,Flutter 下就没有专门测量流程)
就是这样烧脑、但在我看来是不可绕过关键,消耗大量时间来思考和反复论证。
所以前几天有位读者私下感叹说,要是互联网上每篇文章都能像 《这样理解,你也能在 30 秒内讲明白 TCP 三次握手》 一样,能用 "你听得见么" "我听得见" "我也听得见" 让人秒懂三次握手本质该有多好,
实际上在我看来,并不是撰文、教书者 讲解方式 问题,而是 设计系统 和 撰写文档 通常不是同一个人。
我们常常能在源码中看到精妙设计,文档却常常只顾 how 而忽略对 作为重中之重的 如此设计用意 的介绍。
也即,上游设计师 出于各种原因 没有出面解释,导致中游教师或作者 需要花费大量精力 才有一丝丝机会正确理解到。
且从生物代谢角度来说,照本宣科、人云亦云,张口闭口 ACK、SYN(三次握手术语),显然最节能,尽管这样一来他们自己都蒙在鼓里、不知道自己究竟在讲些什么。
TQL、AWSL
于是,在 “现状如此” 背景下,我决定将本篇文章分两部分讲,上半部分着重反思各平台通用 视图系统架构实现全貌,下半部分将对 Android 局部实现 作有选择介绍。
在之前 《自定义 View 导读》 一文中已提到过:源码实现 绝非一蹴而就,它是几十年下来不断演化、不断变更的结果,所以我们务必 首先基于反思 来为自己准备一把切入钥匙,然后在这把钥匙指引下 有目的、点到为止 到源码中去确认,而不是打一开始就盲目一头栽进去 试图事无巨细 全盘通吃。
好了 不多说了,下面让我们跟随 深度思考脚步,从抽象到具体 将视图系统架构实现全貌 无痛过一遍!
视图系统 为何基于 C/S 架构
无论 Android、iOS、还是 MacOS、Windows、Linux,视图系统为实现 输入和输出完美协调,必然需要基于 C/S 架构。
why?
首先,排版结果渲染 和 屏幕事件接收,通用且 使用频率极高,所以它应处于 支持高效率运行、规避数据被篡改或破坏 环境下运作,
根据大学《操作系统》课程,我们易知,进程通常存在两大类:一类是用户进程,一类是系统进程。显然 独立存在 系统进程 能为上述需求提供理想环境:运行时无须申请任何权限、运行效率更高、且规避内部数据惨遭外部篡改 而导致运行结果不可预期风险。
由此视图系统 便不得不 分两部分 运行,一部分是上述提到,常驻 系统进程中的 视图服务,另一部分即是 运行在 用户自己开启用户进程中 视图接口(客户端),客户端 排版结果 和 事件分发,都是通过这个“接口”去向 视图服务 传递和接收。
除上述提到 运行时高效、运行时安全,一分为二设计另一好处在于 实现效率和节省资源平衡:让频繁使用底层服务 常驻、让随机使用客户端 随叫随到、用完即走(关掉 App 进程,进程申请这部分资源 也就归还系统)。
所以 至此我们确知:视图系统需要 “一分为二” 设计 ——
- 让使用频率极高视图服务 常驻系统进程,以减少频繁创建进程导致开销,且由于是在系统进程中运行,因而效率更高、更安全。
- 与此同时,让客户端 在用户进程中生死存亡,从而达到即用即走、节省系统资源目的。
就是因为这需要,造成 “视图接口” 和 “视图服务” 分别处于不同进程,从而有 IPC(Inter-Process Communication,进程间通信)需要。而该场景下效率最高 IPC 通信方式是 RPC(Remote Procedure Call,远程过程调用):一种 C/S 架构实现,因而,视图系统需要基于 C/S 架构。
简单翻看下 Android 源码,如我所料,Stub 和 Session 等字样,几乎实锤 Android 的确是用 RPC 方式完成 该场景下 IPC。