Android事件分发机制抽象--钓钩模型,2024年最新android面试app

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

细节建议读者实操一遍,我直接说结果了:

DOWN 事件:TextView 和 FrameLayout 未消费DOWN事件,会继续向上回传到 DecorView,调用 DecorView 的 onTouchEvent。

但 DecorView 也不消费,继续传给 Activity,调用 Activity 的 onTouchEvent,Activity 返回 false。

简而言之,DOWN 事件会陆续调用到 DecorView 和 Activity,始终没有被消费。

UP 事件:Activity 的 dispatchTouchEvent 先调用到,接着调用 DecorView 的 dispatchTouchEvent。

因为 mFirstTouchTarget 为 null,不会调用 onInterceptTouchEvent,但会设置 intercepted 状态位为 true。逻辑见下述 ViewGroup 中 dispatchTouchEvent 源码片段,执行逻辑为第 4 行和 16 行。

接着调用 DecorView 的 onTouchEvent,显然,DecorView 也不消费,继续传给 Activity,调用 Activity 的 onTouchEvent,Activity 返回 false。

简而言之,UP 事件也不会被消费,而且只会调用 DecorView 和 Activity 的事件分发相关方法,其他控件将无法收到事件分发调用。

ViewGroup 中 dispatchTouchEvent 逻辑源码片段如下图所示:▼

这个问题看似简单,但实际能回答上来的才是真的高手。

画一幅时序图总结一下:▼

但可能有同学会问,不设置按键监听情况下,没啥实际意义,大部分人不会关心这种情况,换一题。

设 按 键 监 听 a n d 拦 截 点 击 分 发 设按键监听 and 拦截点击分发 设按键监听and拦截点击分发

▼ ▼ ▼

2. FrameLayout 和 TextView 均设置按键监听,要求在红色和蓝色区域任意位置点击,只由 FrameLayout 的按键监听响应,怎么做?

这个简单,我来!

重写 FrameLayout 的 onInterceptTouchEvent 方法返回 true。

答案没毛病。但小问题接踵而至,DOWN 事件和 UP 事件可能都会触发调用 onInterceptTouchEvent,上面的答案不区分 DOWN 还是 UP,简单粗暴的返回了 true。DOWN 事件一定要返回 true 吗?返回 false 行不行?UP 事件呢,需不需要返回 true?

2.1 只能拦截 DOWN 事件吗?拦截 DWON 事件后,UP 事件需不需要返回 true?

这里有必要先科普一下按键监听 OnClickListener 的小知识点:

onClick 是由UP 事件的 onTouchEvent 触发调用的,但是触发的前提条件是已标记 PFLAG_PRESSED 按下状态位,而标记操作恰恰是在 DOWN 事件中做的。这也就解释了事件流的连续性。MOVE 事件呢?这是第三题,这里先按下不表。基于上述理论,DOWN 事件是一定要拦截的。但是 UP 事件,想必有部分同学开始模棱两可了,返回 true 肯定对,返回 false 好像也对…

从常识判断,如果一个返回布尔值的纯函数,调用后返回 ture 和返回 false 效果一样,那这个调用肯定是冗余的。onInterceptTouchEvent 基本可以看成是这种纯函数。

基于对 Android Framework 工程师的基本尊重,犯这种低级错误没有道理。

那么结论只能是:onInterceptTouchEvent 在 DOWN 事件返回 true,那么后续 UP 事件根本不会再调用 onInterceptTouchEvent。

换个角度看,如果 onInterceptTouchEvent 在 DOWN 事件返回 true,意味着本控件将拦截处理后续的事件流,后续事件调用自然也就用不着再问要不要拦截。

事实也是如此。

① onInterceptTouchEvent 只能拦截 DOWN 事件,否则 FrameLayout 的按键监听不会响应。

② 无需处理 UP 事件,因为 DOWN 事件拦截后,后续事件流根本不会再调用 onInterceptTouchEvent。因为 FrameLayout 直接在 DOWN 事件就拦截了,TextView 没有机会消费事件,不会有什么问题。但如果我们继续向前走一步,进一步窥探事件分发机制。

2.2 不拦截 DOWN 事件只拦截 UP 事件,UP 事件到底由谁消费?

按理说,DOWN 事件由 TextView 消费,UP 事件被 FrameLayout 拦截,当然是 FrameLayout消费。但如果是这样,TextView 怎么办,考虑过被拦截的子控件的感受了吗?

好比领导给了机会,我也兢兢业业的投入工作,然后就戛然而止…让不让干好歹给个痛快话,我还在干杵着呢…

显然,拦截的控件满意了,但被拦截的控件也不能不管,成熟的事件分发机制必须能妥善解决这些 “民事纠纷”。

这就涉及到了一个高级知识点了-- CANCEL 事件。

这年头,不知道 CANCEL 事件的都不好意思说自己精通事件分发(反正我不敢说精通)。

当 ViewGroup 的子类重写 onInterceptTouchEvent 返回 true 拦截事件后,如果存在被拦截的子控件(该事件流的头部事件已被子控件消费),子控件将会收到一个 CANCEL 事件被告知事件流到此为止。

以上是事件拦截的大致逻辑,但是细心的同学会发现,上面只回答了 CANCEL 事件到哪去,那它是从哪来的呢?被拦截的那个事件,又是谁消费的?

相信这个问题难不倒深入阅读分析事件分发源码的同学,答案如下:

① 被拦截的事件会被转换为 CANCEL 事件,即event.setAction(MotionEvent.ACTION_CANCEL),会传递给被拦截的子控件告知事件流取消,View 中的 onTouchEvent 会消费 CANCEL 事件返回 true。 ② 此后的事件流,将调用拦截控件的 dispatchTouchEvent 和 onTouchEvent。

其实这里面还有一个问题…

2.3 如果拦截了 FrameLayout 的 DOWN 事件,但是不消费,又会怎么样?再这么推演下去,没完没了了,换一题。

磨刀不耽误砍柴,画两幅时序图总结一下:▼

设 按 键 监 听 a n d 按 键 移 动 分 发 设按键监听 and 按键移动分发 设按键监听and按键移动分发

▼ ▼ ▼

  1. 都设置按键监听,在红色区域按下,移动到蓝色区域抬起,谁的按键监听会响应?

这个问题,好像还真没想过…

基于上述按键逻辑,DOWN 事件由 TextView 消费没有争议,关键问题就是第一个不在红色区域但在蓝色区域的 MOVE 事件怎么处理,以及最终的 UP 事件到底是谁消费?

太伤头发了…

分享个生活小妙招放松一下:当我们在按下按钮那一刻,后悔了怎么办?

我的做法是,手按着不放,慢慢移动到按钮以外区域,然后再小心抬起,如愿以偿的没有触发点击操作(终于在付款的最后一刻冷静了下来,机智)。

基于这个常识,上面问题的答案是 FrameLayout 和 TextView 的监听事件均不会调到。

突然想到我爸问过我一个段子:公山羊和母山羊谁有胡子?

我当然没有观察过山羊的胡子,不过问题既然这么问,答案必须是反常识的。

母山羊有胡子,我得意地大声回答。

这时,我爸哈哈大笑,都有胡子…

言归正传,为什么监听事件都不会调用到?

答案都在源码里,我直接公布了:

① DOWN 和红色区域内的 MOVE 事件都由 TextView 消费。

第一个在蓝色区域的 MOVE 事件以及之后的 MOVE 事件和 UP 事件依旧还是 TextView 消费(没想到吧)。

② 如果整个事件流都是 TextView 消费,那么为什么没有响应 onClick?问题的关键在于 MOVE 事件会根据当前坐标是否在控件内来判断是否取消 PFLAG_PRESSED 按下状态位。第一个蓝色区域的 MOVE 事件会将按下状态位标记为未按下(不用机灵地以为移出去再移回来可以响应,没有机会了,MOVE 只能取消按下状态,只有 DOWN 才能标记按下状态)。UP 事件时会检查按下状态位,只有按下情况才会触发 onClick。

③ 过程中不会有 CANCEL 事件,这是一部分同学对 CANCEL 事件的误解。

④ CANCEL 事件产生两个前提条件:子控件已经消费了 DOWN 事件,但父控件拦截了之后的事件。

可能好奇的同学内心还犯嘀咕,会不会有 OnLongClickListener?

3.1 会不会触发 OnLongClickListener?OnClickListener 和 OnLongClickListener 又是什么关系?

这个问题问得好!答案我也直接说了:

① 和 OnClickListener 在 UP 事件触发不同的是,OnLongClickListener 在 DOWN 事件触发,不过不是立即执行,而是延时执行,默认 500ms。

② OnClickListener 和 OnLongClickListener 最多只有一个会执行。

MOVE 事件除了会根据当前坐标是否在控件内来判断是否取消按下状态位,也会来判断是否移除延迟执行 onLongClick。

UP 事件在触发 onClick 前,会检查是否已经执行过 onLongClick 逻辑(注意,是实际执行,不是触发延迟),

如果执行过 onLongClick 监听,则不会触发 onClick,

如果没有执行过 onLongClick 监听,会先移除延迟执行 onLongClick 再触发 onClick。

拦截产生的 CANCEL 也会移除延迟执行 onLongClick。还是老规矩,画一幅时序图总结一下:▼

总 结 总结 总结

▼ ▼ ▼

受《Android 开发艺术探索》的启发,尝试使用简明扼要的伪代码来总结回顾一下事件分发机制。

事件分发的难点在于一连串的事件流,把单点的独立问题变成了多点的连续问题,而且所有控件都走这套逻辑,目不暇接难免稀里哗啦稀里糊涂。

致敬玉刚哥的“领导分配任务”流程解释事件分发机制,我也尝试总结一下:

来了个项目,领导优先“分发”下去,问你接不接?

当然,没有人能强迫你,你可以不接(这对应事件分发不消费场景)。那后果就是,没有然后了,你不干有的是人干,机会只有一次。

所以你信心满满地对领导说,我好好干(这对应事件分发消费场景)。

然后这个项目的人力物力财力都会源源不断(一个项目对应一个完整事件流)给到你,大家都开心。

过了一段时间,领导发现项目不及预期,找你来了场触及灵魂的沟通。

最后领导和你说,现在我来负责这个项目(这对应事件拦截),你好好休息一段时间(这是你收到的 CANCEL 事件)。

后续的资源不断调拨给领导(对应拦截后的事件流改道),领导也没得选,只能自己加班加点干(拦截事件流后要对事件流负责到底,不论你干不干,这就是“项目闭环”)。

公司管这叫“补位”。“分发”和“补位”是领导的基本素质。

小伙伴可能会说,枯燥。现在好像懂了,过两天只能假装懂了,过一段时间可能就忘得精光。

有没有简单又好记的一个模型或者一幅图,方便让我们想起生活更美好的那种。

我也思索过这个问题,但没有找到答案,所以,我尝试挑战一下。

通过观察事件分发流程,发现有点像钓鱼:

第一步先放下鱼饵等鱼上钩(DOWN 事件分发),找到最终的一个 U 型路径,有点像钓鱼钩,这也是模型名称的由来。

第二步将鱼线收回来(MOVE / UP / CANCEL 事件分发),这个阶段是否消费(onTouchEvent 返回值)不重要,重要的是是否拦截(onInterceptTouchEvent),拦截只能是在当前 U 型路径上截断,而且拦截后不再调用该控件的拦截方法。

这个过程有点像你把鱼线直线往回拉,正常情况下这条鱼是你的了,但是意外的惊喜是有条大鱼把你鱼钩上的鱼当成了鱼饵,这你发财了,因为钓到的是这条更大的鱼(放长线钓大鱼)。

想想是不是这么回事,你抓住了这个长得像鱼钩的 U 型路径,是不是也就能对事件分发的各种问题给出答案了。逻辑详情如下图所示:▼

引用柳宗元《江雪》为本篇做个结尾:孤舟蓑笠翁,独寒江雪。

喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗😜😜😜~
点击【GitHub】还有彩蛋哦!!!

尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

* (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-YxKFmfAO-1713707427287)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-9UBPhSNm-1713707427287)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 29
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值