Android 事件分发面试题

序、慢慢来才是最快的方法。

Android 2020年面试系列(02 — View事件分发)_view事件分发 2020-CSDN博客

1. Touch事件如何从屏幕到我们的App。

硬件与内核部分

当我们触摸屏幕或者按键操作时,首先触发的是硬件驱动
驱动收到事件后,将相应事件写入到输入设备节点,这便产生了最原生态的内核事件
当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下

这样做的目的是将输入事件封装为通用的Event,供后续处理。

SystemServer部分

我们知道,当系统启动时,在SystemServer进程会启动一系列系统服务,如AMS,WMS等。

其中还有一个就是我们管理事件输入的InputManagerService。这个服务就是用来负责与硬件通信,接受屏幕输入事件。

跨进程通信传递给App

现在系统进程已经拿到输入事件了,但还需要传递给App进程,这就涉及到跨进程通信的部分
我们的App中的Window与InputManagerService之间的通信实际上使用的InputChannel
InputChannel是一个pipe,底层实际是通过socket进行通信。
我们知道在Activity启动时会调用ViewRootImpl.setView()
在ViewRootImpl.setView()过程中,也会同时注册InputChannel:

2.Touch事件到达App后怎么传递到对应页面

事件回传到ViewRootImpl

事件到达应用端的主线程,会通过ViewRootImpl进行一系列InputStage来处理事件。这个阶段其实是对事件进行一些简单的分类处理,比如视图输入事件,输入法事件,导航面板事件等等。
我们的View触摸事件就发生在ViewPostImeInputStage阶段

可以看到事件分发经过了:DecorView -> Activity -> PhoneWindow -> DecorView

  • 为什么ViewRootImpl不直接把事件交给Activity?

主要是为了解耦
ViewRootImpl并不知道有Activity这种东西存在!它只是持有了DecorView。所以,不能直接把触摸事件送到Activity.dispatchTouchEvent()

  • 交给Acitivity后,为什么不直接交给DecorView开始分发事件呢?

因为Activity不知道有DecorView!但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView。
在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了,Activity不需要知道Window中的具体内容

3. Touch事件到达对应页面后内部怎样分发

1. ViewGroup是否拦截事件

public boolean dispatchTouchEvent(MotionEvent event) {boolean isConsume = false;if (isViewGroup) {if (onInterceptTouchEvent(event)) {isConsume = super.dispatchTouchEvent(event);} } return isConsume;
}

如果是ViewGroup,会先执行到onInterceptTouchEvent方法判断是否拦截,如果拦截,则执行父类View的dispatchTouchEvent方法。

如果ViewGroup不拦截,则会传递到子View

如果不拦截,ViewGroup内主要做以下几件事


1. 遍历当前ViewGroup的所有子View


2. 判断当前View是否在当前子View的坐标范围内,不在范围内不能接收事件,直接跳过


3. 利用dispatchTransformedTouchEvent,如果返回true,则通过addTouchTarget对mFirstTouchTarget赋值


4. dispatchTransformedTouchEvent做的主要就是两个事,如果child不为null,则事件分发到child,否则调用super.dispatchTouchEvent,并最终返回结果


5. mFirstTouchTarget是单链表结构,记录消费链,但是在单点触控的时候这个特性没有用上,只是一个普通的TouchTarget对象

2. 子View是否拦截

子View的diapatchTouchEvent逻辑比较简单

  • 如果设置了setOnTouchListener并且返回为true,那么onTouchEvent就不再执行

  • 否则执行onTouchEvent,我们常用的OnClickListenr就是在onTouchEvent里触发的

所以默认情况下会直接执行onTouchEvent,如果我们设置了setOnClickListener或者setLongClickListener,都会正常触发。

  • 如果子View消费事件会怎么样?

上面说了,如果子View消费事件,即dispatchTouchEvent方法返回true
表示这个事件我处理了,那么事件从此结束,ViewGroup的dispatchTouchEvent也返回true

  • 如果子View不消费事件会怎么样?

子View不拦截事件,那么mFirstTouchTarget就为null,退出循环后,调用了dispatchTransformedTouchEvent方法。

最后回到Activity的dispatchTouchEvent,也是直接返回true

如果ViewGroup与子View都不拦截会怎么样

如果ViewGroup与子View都不拦截,即mFirstTouchTarget == null,dispatchTouchEvent也返回false
再看看Activity的源码

public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {onUserInteraction();}if (getWindow().superDispatchTouchEvent(ev)) {return true;}return onTouchEvent(ev);}

答案很明显:会执行Activity的onTouchEvent方法

小结

  • 事件分发的本质就是一个递归方法,通过往下传递,调用dispatchTouchEvent方法,找到事件的处理者,这也就是项目中常见的责任链模式。

  • 在分发过程中,ViewGroup通过onInterceptTouchEvent判断是否拦截事件

  • 在分发过程中,View的默认通过onTouchEvent处理事件

  • 如果底层View不消费,则默认一步步往上执行父元素onTouchEvent方法。

  • 如果所有View的onTouchEvent方法都返回false,则最后会执行到Activity的onTouchEvent方法,事件分发也就结束了。

4.滑动冲突解决

常见的滑动冲突解决方法有两种:
1. 外部拦截法
2. 内部拦截法

外部拦截法

外部拦截法的原理很简单,就是通过我们上面分析的onInterceptTouchEvent进行。外部拦截法的模板代码如下:

内部拦截法

内部拦截法是将主动权交给子View,如果子View需要事件就直接消耗,否则交给父容器处理
内部拦截法主要通过requestDisallowInterceptTouchEvent方法控制

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {final boolean intercepted;//只有ActionDown或者mFirstTouchTarget为空时才会判断是否拦截if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev);} } }

如上所示,原理很简单
1. 子View通过requestDisallowInterceptTouchEvent控制mGroupFlags的值,从而控制disallowIntercept的值
2. disallowIntercept为true时就不会走到onInterceptTouchEvent,外部也就无法拦截了,当需要外部处理时,将disallowIntercept置为false即可

5.每一个事件的分发都需要递归吗?用户一次操作会产生大量的 UI 事件,频繁的递归遍历不会对性能有影响吗?

当然会有!所以 Android 为了避免每个事件都递归遍历,定义了一个 【事件序列】 的概念:将用户每一次触摸屏幕 --> 移动屏幕-->抬起手指称为一个事件序列。

一个事件序列必然包含 ACTION_DOWN,ACTION_MOVE,ACTION_UP 等多个事件。其中 ACTION_MOVE 数量不确定,ACTION_DOWN 和 ACTION_UP 数量则为 1

当接收到 ACTION_DOMN 事件时,意味着一次完成事件序列的开始。ViewGroup 会通过递归遍历找到 View 树中真正对事件进行消费的子 View,并将其保存。这之后接收到 ACTION_MOVE 和 ACTION_DOWN 事件时,则跳过递归遍历的过程,直接交给之前保存的消费者。当下一次 ACTION_DOWN 事件来临时重置整个过程。

6.CTION_CANCEL 事件是用来干嘛的?

如果有 View 能够消费事件,那么该事件序列所有的后续事件都会交给这个 View 处理。但如果不希望它处理全部的后续事件怎么办?比如手指点击一个 Button 然后滑出它的边界。在这个事件序列中,我只希望 Button 处理它边界内的 move 事件。对于边界外的 move 事件,虽然它们都在一个事件序列中,但理论上不应该再传递给 Button 了。

ACTION_CANCEL 就是用来解决这个问题的。当 Button 判断 move 事件已经超出 view 的边界时,会将自己的 mPrivateFlags 置为 cancel 状态。等下次事件分发来临,Button 的父 ViewGroup 会检测 Button 的 mPrivateFlags,如果为 cancel 则将之前保存的 mFirstTouchTarget(也就是指向 Button 的引用) 设为 null,并向 Button 发送一个 ACTION_CANCEL 事件,表示以后不会再接受事件了。

参考:

【面试官爸爸】唠唠Android事件分发? - 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/126488.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何创建 SpringBoot 多模块项目

1. 创建父模块 【添加依赖】 【删除父模块资源】 父模块只需要保留 pom.xml,其他文件的全部删除(包括 src) 2. 创建子模块 3. 修改父模块 3.1 删除不必要的依赖 3.2 添加打包类型 3.3 添加所有子模块 声明子模块有两个好处: …

Pytorch 注意力机制解析与代码实现

什么是注意力机制 注意力机制是深度学习常用的一个小技巧,它有多种多样的实现形式,尽管实现方式多样,但是每一种注意力机制的实现的核心都是类似的,就是注意力。 注意力机制的核心重点就是让网络关注到它更需要关注的地方。 当…

什么是 CNN? 卷积神经网络? 怎么用 CNN 进行分类?(3)

参考视频:https://www.youtube.com/watch?vE5Z7FQp7AQQ&listPLuhqtP7jdD8CD6rOWy20INGM44kULvrHu 视频7:CNN 的全局架构 卷积层除了做卷积操作外,还要加上 bias ,再经过非线性的函数,这么做的原因是 “scaled p…

电压放大器在压电陶瓷致动器中的应用有哪些

电压放大器在压电陶瓷致动器中有多种应用。压电陶瓷致动器是一种能够将电能转化为机械能的装置,通过施加电压来使陶瓷材料发生形变或振动。它在许多领域中得到广泛应用,如精密定位、振动控制、压力控制等。下面安泰电子将详细介绍电压放大器在压电陶瓷致…

java修仙基石篇->instanceof子父类检查

instanceof检查子父类(或者是否能被强转) 作用1:检查某对象是否是某类的子类 如:儿子类继承了父亲类。 检查儿子类对象是否属于父亲类 作用2:检查两个对象是否可以强转 语法: 子类对象 instanceof 父…

物联网智慧种植农业大棚系统

物联网智慧种植农业大棚系统 项目背景 智慧农业是是将物联网技术和农业生产箱管理的新型农业,依托部署在农业生产现场的各种传感节点,以物联网网关为通道形成数据传输网络,可以实现控制柜、环境监测传感器、气象监测机器等设备的远程监控&a…

【开题报告】基于SpringBoot的医美在线预约系统的设计与实现

1.研究背景 医美行业是指结合医学和美容技术,为人们提供外貌改善和整容手术等服务的领域。随着社会经济的发展和人们审美观念的变化,医美行业得到了快速的发展,并受到越来越多人的关注和需求。 传统的医美预约方式主要依赖于电话预约或现场…

大数据之LibrA数据库系统告警处理(ALM-12006 节点故障)

告警解释 Controller按30秒周期检测NodeAgent状态。当Controller连续三次未接收到某个NodeAgent的状态报告时,产生该告警。 当Controller可以正常接收时,告警恢复。 告警属性 告警ID 告警级别 可自动清除 12006 严重 是 告警参数 参数名称 参…

【计算机网络】数据链路层——以太网

文章目录 前言什么是以太网以太网帧格式6位目的地址和源地址2位类型数据长度CRC 校验和 数据在数据链路层是如何转发的 前言 前面我们学习了关于应用层——自定义协议、传输层——UDP、TCP协议、网络层——IP协议,今天我将为大家分享关于数据链路层——以太网方面的…

C++ 如何快速确定新旧线程

在C中,您可以使用一些方法来快速区分是否当前代码正在主线程中执行还是在一个新线程中执行。以下是一些方法: std::this_thread::get_id(): 使用std::this_thread::get_id()可以获取当前线程的唯一标识符。您可以将主线程的ID与新线程的ID进行…

C语言 DAY08 指针01

1.概述 地址编号:地址编号:就是计算机为了存储数据,每一个程序在32机中占4G,以一个字节为最小单位进行操作,每一个字节都有其对应的地址,该地址就是地址编。 指针:地址编号的数据类型 指针变量:存储地址编号的变量,其数据类型为指针 在32位…

【Java-代码-A02】(00) 通过Java遍历文件夹,快速上手;

前言 【描述】 通过"Java"遍历文件夹下的所有文件,快速上手; 【环境】 系统"Windows",软件"IntelliJ IDEA 2021.1.3(Ultimate Edition)";“Java版本"1.8.0_202”; 实操 【第一步…

SQL练习(牛客网非技术快速入门)

SQL3 查询结果去重 题目:现在运营需要查看用户来自于哪些学校,请从用户信息表中取出学校的去重数据。 示例:user_profile iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male复旦大学Shanghai36543female20北京大学Beijing4…

Python武器库开发-常用模块之OS模块(十一)

常用模块之OS模块(十一) Python中的 os 模块提供了非常丰富的方法用来处理文件和目录,可以执行一些操作系统的功能。常用的方法如下表所示: 序号方法描述1os.access(path, mode)检验权限模式2os.chdir(path)改变当前工作目录3os.chflags(path, flags)设…

B-5:网络安全事件响应

B-5:网络安全事件响应 任务环境说明: 服务器场景:Server2216(开放链接) 用户名:root密码:123456 1.黑客通过网络攻入本地服务器,通过特殊手段在系统中建立了多个异常进程,找出启动异常进程的脚本,并将其绝对路径作为Flag值提交; 通过nmap扫描我们发现开启了22端口,…

JAVA学习笔记——接口

概念: 接口(Interface)是一种规范或协议(Protocal),是由常量和抽象方法组成的特殊类,是对抽象类的进一步抽象,用于克服 Java 单继承的缺点。例如:每个厂商在生产鼠标的时候,鼠标的接口遵循了 USB 接口统一标…

C++特殊类的设计

文章目录 设计一个类不能被拷贝请设计一个类,只能在堆上创建对象设计一个类只能在栈上去创建对象设计一个类不能被继承设计一个类,只能创建一个对象(单例模式)饿汉模式懒汉模式 单例模式总结饿汉模式懒汉模式 设计一个类不能被拷贝 拷贝一个类对象可以有…

Kubernetes 概述以及Kubernetes 集群架构与组件

目录 Kubernetes概述 K8S 是什么 为什么要用 K8S K8S 的特性 Kubernetes 集群架构与组件 核心组件 Master 组件 Node 组件 ​编辑 Kubernetes 核心概念 常见的K8S按照部署方式 Kubernetes概述 K8S 是什么 K8S 的全称为 Kubernetes,Kubernetes 是一个可移植、可扩…

面试算法45:二叉树最低层最左边的值

题目 如何在一棵二叉树中找出它最低层最左边节点的值?假设二叉树中最少有一个节点。例如,在如图7.5所示的二叉树中最低层最左边一个节点的值是5。 分析 可以用一个变量bottomLeft来保存每一层最左边的节点的值。在遍历二叉树时,每当遇到新…

解决‘BaichuanTokenizer‘ object has no attribute ‘sp_model‘,无需重装transformers和torch

如https://github.com/baichuan-inc/Baichuan2/issues/204 中所说: 修改下 tokenization_baichuan.py ,把 super() 修改到最后执行 self.vocab_file vocab_fileself.add_bos_token add_bos_tokenself.add_eos_token add_eos_tokenself.sp_model spm…