View 事件分发机制,大型职场 PUA 现场 | 创作者训练营

3,559 阅读10分钟

前言

很高兴见到你 👋

我不是大佬,但我相信我在通往成为大佬的路上 | 掘金年度征文 中我结合自身经历阐述了自己对学习方法的思考与实践。简单来说,我认为学习需要建立一套系统的知识体系(知识树),在此基础上可将学习分为 通用学习需求学习。本文将简单讨论掌握知识的过程,并基于这一过程的各阶段进行内容创作实践。

掌握知识的三个阶段

个人认为,对知识的掌握,需要经历以下过程:

  • 感性认识阶段
  • 理性认识阶段
  • 实践阶段

感性认识 可以看作是一个 从 0 到 1 的过程,它是认识的初级阶段。在这一过程学习者通常会寻找已有的素材学习(如书籍,论坛博客,视频等)。而创作感性认识相关的内容看似简单,实则非常困难。这里列举一些优质的从 0 到 1 的内容的创作者:

点击查看详情

理性认识 建立在 感性认识 的基础上,在这一过程学习者通常会在头脑中建立起相应的知识体系并进行从 1 到 N 的扩展思考。具体些便是通过阅读源码等手段对原理进行分析并结合已有知识体系进行总结归纳。这部份内容的创作很容易陷入创作者「自说自话」的怪圈,而读者会觉得「不知所云」。

实践阶段 是整个过程的终点同时又是起点。之所以这么说是因为该过程并非简单的线性的结构,而是一个循环的过程。

通过具体的实践,不仅可以避免「一听就会,一做全废」的尴尬局面,还是对前面认识阶段的升华,是进入更高层级认识阶段的起点。

今天我将以 View 的事件分发机制 这一知识为切入点来进行内容创作。本文的定位为建立感性认识阶段。

前人栽树好乘凉

关于 View 的事件分发机制,个人认为写的最好的两篇文章分别是:

KunMinX 的文风简洁干净,清楚地表述出 View 事件分发机制 的本质以及这一过程中的「消费」这一概念的理解。

却把清梅嗅 则站在设计者的角度,阐述了事件分发的全貌,其中 View 的事件分发机制 只是 UI 层事件分发的一个环节。

基于前一节的理论,掌握 View 的事件分发这一内容的过程可以这样拆分:

感性认识阶段:

  • 确认该知识的在「知识树」的位置:是 Android 中 UI 层事件分发的一个环节

  • 在脑海中建立该内容的模型:

    1. Android 中的视图树是一个 N 叉树的模型
    2. N 叉树最底部的节点是最靠近用户的视图,响应用户操作的优先级较高
    3. 响应用户操作本质上是在 N 叉树上寻找特定节点
    4. N 叉树常用的遍历的方式是递归
    5. 为了提高性能,可以根据终止条件提前结束递归
    6. 为了提高性能,可以使用「拦截」的手段避免每次遍历全部节点
    7. ...

感性认识的深度与学习者所处知识层级有关,感性认识 → 理性认识 → 实践 是一个循环的过程,因此不同阶段的学习者感性认识的深度不同。

理性认识阶段:

脑中建立模型后可以进行一些细节的学习,该过程学习者会进行从 1 到 N 到扩展思考与学习:

例如在查看源码实现之前,如果不清楚 N 叉树的遍历方式或者不理解递归算法的流程,很可能会被「消费」,「返回 true」等描述搞得晕头转向。

  • 学习递归的概念以及写法
  • 学习实现拦截的常用手段:责任链模式
  • 查看具体的源码实现

同样,处于不同知识层级的学习者对理性认识的深度也不同。因此对于源码的阅读,开始可以过滤掉细节,理清核心逻辑(简单的分发 + 拦截),后续关注之前忽略掉的细节(如多指的处理)。

实践阶段:

对于前面的认识进行实践,例如自定义 ViewGroup 和 View,通过打日志等手段验证之前的学习内容,并在这一过程加深之前的认识并进入下一次循环。

本文的定位为感性认识阶段,适合对 View 事件分发机制理解不清晰,或者之前不知道该内容的读者阅读。

正文开始。

是大型职场 PUA 的现场啊

严格的等级制度

某公司有着严格的等级制度,模型类似 N 叉树:

该公司的员工可分为两种职位:View 和 ViewGroup。

  • View 职位的员工属于最底层的员工,权限最小
  • ViewGroup 职位的员工手下可以管理其他员工(有混得惨的,手下没有员工,如 ViewGroup4)
    • 从权限的角度讲:ViewGroup 拥有自己特殊的权限(管理手下)和 View 的权限
    • 从角色的角度讲:ViewGroup 作为上级时,充当 ViewGroup 角色,作为下级时,充当 View 的角色

除了上述通用的职位,还有一些特殊的「大佬级别」的职位:

  • ViewRootImpl:该公司对外业务的职位,唯一。当一个任务来临时,会先交给 ViewRootImpl,下属为 DecorView
  • DecorView:从 ViewGroup 晋升的职位,唯一。下属为 老大(Activity)安排的 ContentView
  • Activity:该公司老大,唯一。下属为 DecorView

该公司不仅有着严格的 等级 制度,还有着严格的 保密 制度。

严格的保密制度

每个员工只能和自己的上级/下级通信,并且上级并不了解下级的能力以及擅长的方向

因此,当一个任务来临时,上级不知道这项工作下级能不能处理。

Android 的应用世界有着若干个这样的公司。

处理任务的流程

由于前面的各种制度制约,该公司接到任务后的处理流程可以抽象为:

从 N 叉树的根节点开始,遍历整个 N 叉树,寻找一个能够处理该任务的节点

该公司处理任务的具体流程如下:

  • 一个任务被拆为多个指令,即任务是一个抽象概念,真正的载体是一个个的 任务指令
    • 「任务开始」指令意味着这是一个新任务的开始,后续的指令均是该任务的指令
    • 任务进行的过程会存在多个「任务进行」的指令
    • 最终任务以「任务结束」指令结束
  • 为了锻炼员工,任务优先交与下层员工处理
  • ViewGroup 的职责是收到上级的任务指令后,拥有两个选择:
    1. 自己直接处理该指令,不通知下级(即拦截),或者没有下级,只能自己处理,并将处理结果反馈给上级(处理结果 OK 或 不 OK)
    2. 通知下级处理该指令
      • 如果下级反馈处理结果 OK,则直接向上级交差(反馈结果 OK)
      • 如果下级反馈结果不 OK,则自己处理(充当了 View 的角色),并将处理结果反馈给上级(处理结果 OK 或 不 OK)
  • View 收到上级的指令后,会进行处理并将处理结果反馈给上级(处理结果 OK 或 不 OK)
  • 如果从 DecorView 遍历一遍后处理结果仍不 OK,则 DecorView 会将任务指令上报给老大 Activity

拦截流程

ViewGroup 拥有拦截任务指令的权限,拦截分为以下情况;

  • 当拦截的任务指令是「任务开始」时,下属根本不知道这个任务的存在,因此该任务的后续的指令不会交予下属处理
  • 当拦截的任务指令时 「任务进行」时,ViewGrroup 会接管该任务,并且使用「任务取消」的指令通知下属(别忙乎了,该任务我处理了)

由于 ViewGroup 拥有拦截任务的权限,这样会导致下级没有机会表现,因此公司赋予了下属可以「拒绝上级拦截」的权限

  • 当上级拦截的任务指令是「任务开始」时,下属根本不知道任务的存在,因此没机会使用「拒绝上级拦截」
  • 当上级拦截的任务指令是「任务进行」时,下属可以向上级「主动请缨」,使用「拒绝上级拦截」,这样上级的拦截无效了

优化

一个任务由多个任务指令组成,如果每个任务指令来临都要遍历一次整个公司的所有员工,处理时间太长。

因此可以进行一些优化,减少遍历的次数。

如上图所示,如果一个任务的「任务开始」ViewGroup4 的处理结果 OK,那么该任务的后续指令可以无需遍历完整的树,而直接将该任务的后续指令直接交予 ViewGroup1 → ViewGruop4 这一分支(红色标识)。

动画演示

上图演示了没有拦截并且遍历所有节点的情况。

DecorView -> Activity ->PhoneWindow 这一流程可参考 Android 事件分发机制的设计与实现 DecorView 的双重职责 一节。

总结

  • View 的事件分发机制 核心是 从 N 叉树的根节点开始,遍历整个 N 叉树,寻找一个能够处理该事件的节点
  • 任务-任务指令 模型对应着「事件序列」模型:
    • 任务开始ACTION_DOWN
    • 任务进行ACTION_MOVE
    • 任务结束ACTION_UP
    • 任务取消ACTION_CANCEL
  • 所谓的返回 true / false 代表着执行结果 OK / 不 OK
  • 上级 ViewGroup 拥有拦截能力,下级 ViewGroup/View 拥有让上级取消拦截的能力
  • 如果 ViewGroup 从 任务开始ACTION_DOWN 便拦截了 任务/事件,则 下级 ViewGroup/View 没机会让上级取消拦截
  • 为了提高性能,一个任务的后续指令会优先使用上次遍历结果 OK 的那条路径,对应模型为 TouchTarget(责任链模式的存储处理器模型)

下期内容将进入理性认识阶段,介绍 递归遍历 N 叉树的方式,责任链模式的常用实现,以及 View 事件分发的源码简析。

关于我

我是 Flywith24,我的博客内容已经分类整理 在这里,点击右上角的 Watch 可以及时获取我的文章更新哦 😉

小专栏         Github           语雀

自建博客     CSDN     Bilibili

目前我正专注于建立系统化的知识体系,内容更新在语雀上。感兴趣的小伙伴可以在相关文档底部评论区补充优质的资源以及技术细节。点击查看

View 事件分发机制,大型职场 PUA 现场 | 创作者训练营 征文活动正在进行中......