Android系统输入事件分发详解
什么是输入事件?
我们知道,运行android系统的设备本质上是一台计算机,使用者在和计算机进行交互的时候可以抽象成简单的对计算机的输入和输出(IO)。那么对于运行在计算机上的操作系统来说,操作系统在与使用者进行交互的时候起始也是可以抽象成对外界的输入进行处理,然后在输出返还给使用者。本文只讨论的是android系统中的“输入事件”(因为本文讨论的都是输入事件,所以以下简称“输入事件”为“事件”)相关的内容。
根据以上描述,我们就可以回答上面的问题了:
事件是计算机设备接收到的输入信息。
有了以上的定义,我们先来研究下事件是怎么产生的?
在Android开发
系统中,事件是由用户的动作触发的,进而在android硬件层引起硬件状态的变化,android底层会将硬件的状态的变化封装成更加高级的类对象并传递到android 的Framework层,Framework会继续把事件传递给应用层(确切地说是应用层里面应用开发者编写的具体的View的子类对象),最终应用层层会根据事件来进行响应并将响应结果通过对硬件的调用反馈给用户。在以上的整个过程中,应用开发者只需要关注其中的一小部分就可以了,那就是事件从Framework层传递到应用层之后我们该怎么处理这些事件。当然,本文也主要讨论这些内容。
借用以下android 架构图来展示下事件从底层到应用层经历了多少过程。android架构图
注意:事件的传递并不是只传递一个单一的事件的,每一次用户的动作都会产生一个有序的事件列表,这个有序的事件列会被按顺序一次传递到应用层。
输入事件类型
android中所有的事件被抽象成了一个Java类,InputEvent。它有两个直接子类,MotionEvent和 KeyEvent,分别代表了动作事件(通常由触摸屏所产生,也是最常见的)的事件和按键所产生的事件。但是事实上InputEvent不止有这两种。因为android系统可运行的设备类型非常丰富,在不同的设备上对应不同的硬件,肯定也就回有不同的输入事件了,InputEvent按照事件的输入来源不同可以分为以下几类(非全部):
Sensor InputEvent(传感器输入事件)
Hook InputEvent(耳机Hook键输入事件)
On InputEvent(开机键输入事件)
Touch Screen InputEvent(触摸屏输入事件,也是本文主要介绍的)
Key InputEvent(按键输入事件,Home/Menu/Back,电视遥控器按键等)
Hover InputEvent(鼠标动作输入事件)
一般来说,android系统的运行设备是手机或者平板电脑,它们都会有触摸屏,最常见的输入事件还是Touch Screen InputEvent了。本文将会主要介绍TouchScreen InputEvent 和 Key InputEvent。android系统的输入设备列表参考这里:输入设备
Touch Screen InputEvent的处理和传递
Touch Screen InputEvent基础
Touch Screen InputEvent被封装成了一个java类-MotionEvent,它包含三个基础的事件,其它的事件都是在者三个事件的基础之上进行组合封装而成的,例如,click事件就是一个ACTION_DOWN 和一个 ACTION_UP的组合。
基础事件:
ACTION_DOWN 手指按到屏幕上
ACTION_MOVE 手指在屏幕上移动
ACTION_UP 手指从屏幕上离开
事件序列:
上文以及提到过,Touch Screen的事件并不是单个出现的,用户的每次交互都会产生一个事件序列,这个序列会以一个ACTION_DOWN开头,中间有0 或者 n个ACTION_MOVE事件,最后以一个ACTION_UP结尾。一个事件序列往往可以组成更加高级的事件,比如说点击事件、双击事件、手势事件等,View在消费这些事件的时候也会以一个独立的事件序列作为一个最小单位来消费,而不是一个个的基础事件。
所以,我们可以认识到,Touch Screen的事件是可以分为两种类型的:
基础类型:包含ACTION_DOWN、ACTION_MOVE、ACTION_UP三个。
复合类型:由基础类型的事件序列组合称一个代表了用户特定的输入信息的事件。
几个关键方法及其其作用
在软件开发中,当我们写下一个方法后,我们的目的可能是使用该方法完成某些功能,也有可能是为了程序的可扩展性等其它的理由。打个比方来说,在android的常用组件Activity类中,有一个方法叫做setContentView(),它的作用是给当前的Activity指定一个布局来显示,这样的方法就是我们刚提到的“来完成某些功能”的方法;Activity中还有一个叫做onCreate() 的方法,它的作用是一个占位的方法,表示现在是Activity正在创建的时机,可以在子类来重写该方法完成一些初始化工作,像这类方法并没有完成某些具体的功能,就是我们提到的“为了程序的可扩展性等其它的理由”。为了方便起见,在本文中我将会称第一种方法为“功能型方法”,称第二种为“结构型方法”。但是有些方法并不是简单地确认为某一种类型,它可能两种特性都包含。还以以上提到的Activity的onCreate方法为例,首先,它是个结构型方法,因为它的目的包括通知子类进行初始化工作,其次,它本身也进行了一部分对Activity的初始化,这样来说,他就也完成了某些功能,也可以说是一个功能型方法,事实上onCreate两者都是(以下简称该类方法为Both)。
在MotionEvent的处理和传递过程中有几个关键的方法,他们分别出现咋View、ViewGroup、Activity、Window等多个类中,但是名字则是完全一致,功能也是非常接近的。
以上提到了几个关键方法是MotionEvent事件分发和处理的关键所在,它们涉及到几个类:
Activity
Window
View
ViewGroup
那么,MotionEvent事件是如何在这几个类之间传递的呢?而且我们以及知道了事件是由Framework层传递到应用层的,那么究竟是谁从Framework层传递过来,又是谁在应用层接收到了呢?
通过查看android SDK的源码,可以发现是WindowManagerService这个关键类把事件从Framework层传递到应用层的,并且通过PhoneWindow的成员DecorView传递到了当前的Activity中(具体详情请参考Android FrameWork——Touch事件派发过程详解,本文不再赘述)。也就是说是WindowManagerService做了Framework层和应用层的桥梁,把MotionEvent事件传递给了当前的Activity,当前Activity是应用层最先接收到MotionEvent事件的类。
这时候,就可以理解为什么在Activity中有dispatchTouchEvent 和 onTouchEvent这两个类了,因为Activity是应用层事件分发的第一站。
我们已经了解到了MotionEvent是怎么通过Activity传递到View中的,而android的View层是一个拥有树状结构的View树,树的每一个节点也都是一个View,那么MotionEvent是怎么在View树内部传递的呢?
首先,与该事件相关的根View的dispatchTouchEvent会被调用,一次来在该View内分发事件,事件会被分发给该类的onTouchEvent方法、该View的OnTouchListener的onTouch方法、以及子View的dispatchTouchEvent方法。View会依据以上所提到的三个目标方法的返回值来觉得dispatchTouchEvent的最终返回值。
然后,当事件被分发到onTouchEvent方法中以后,该View会对事件进行处理,View的OnClickListener的onClick方法便是在这里调用的。
假如说该View是一个ViewGroup,那么它的onInterceptTouchEvent方法会在dispatchTouchEvent方法执行的时候被调用,并根据是否打断事件传递来觉得dispatchTouchEvent的返回值。
以下伪代码可以清晰地展示它们三者的关系:
Java
public booleandispatchTouchEvent(MontionEvent e){
boolean consume = false;
if(onInterceptTouchEvent(e)){
consume = onTouchEvent(e);
}else{
consume = child.dispatchTouchEvent(e);
}
return consume;
}
public booleandispatchTouchEvent(MontionEvent e){
boolean consume = false;
if(onInterceptTouchEvent(e)){
consume = onTouchEvent(e);
}else{
consume = child.dispatchTouchEvent(e);
}
return consume;
}
当然,事实上View中的dispatchTouchEvent要复杂的多,它需要处理的东西非常多,这里我们就只挑主要的介绍了。
MotionEvent事件序列是被当做一个整体来处理的,假如一个View不消耗一个ACTION_DOWN事件,那么随后的其它该序列的事件也将不会传递给它。
当一个View觉得要处理某个时间序列的话,该事件序列的所有子事件都将会被交给它处理。
ViewGroup的onInterceptTouchEvent默认返回false,也就是说ViewGroup默认是不拦截事件的。
Key InputEvent的处理和传递
Key InputEvent 基础
在应用层,Key InputEvent被封装成了一个叫做KeyEvent的类,它想MotionEvent一样,也是InputEvent的一个子类。总的来说,KeyEvent在应用层的传递方式和Motion Event是一致的。都是由WindowManagerService获取到事件,由DecorView调用ViewRoot,进而按照Activity-Window-View树的流程传递事件的。
类似于MotionEvent的关键方法,KeyEvent的关键方法分别是:
dispatchKeyEvent
onKeyEvent
onInterceptKeyEvent
在View树中的传递过程与Touch事件的一致,这里不再赘述。
以上所提到的只是Android开发
中事件分发机制的大致轮廓,具体的代码则要复杂的多,大家有时间的话可以研究下具体的源码,相信大家都会有很大的收获的。
Android系统输入事件分发详解的更多相关文章
- Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习
package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.V ...
- Android事件分发详解(六)——ACTION_DOWN的消费验证
MainActivity如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...
- Android系统--输入系统(十七)Dispatcher线程_分发dispatch
Android系统--输入系统(十七)Dispatcher线程_分发dispatch 1. 回顾 InputRead线程从输入设备当中得到输入事件 对于读到输入事件稍作处理,比如紧急事件,来电时候按下 ...
- Android系统--输入系统(八)Reader线程_使用EventHub读取事件
Android系统--输入系统(八)Reader线程_使用EventHub读取事件 1. Reader线程工作流程 获得事件 size_t count = mEventHub->getEvent ...
- Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件
Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件 1. 输入按键 我们知道Android系统的按键分为三类:(1)Global Key;(2)Syste ...
- [Android新手区] SQLite 操作详解--SQL语法
该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法 :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...
- Android系统--输入系统(九)Reader线程_核心类及配置文件
Android系统--输入系统(九)Reader线程_核心类及配置文件 1. Reader线程核心类--EventHub 1.1 Reader线程核心结构体 实例化对象:mEventHub--表示多个 ...
- Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析
Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析 0. 前言 个人认为该知识点阅读Android源代码会不仅容易走进死胡同,并且效果并不好,前脚看完后脚忘记,故进行总结, ...
- Android App优化之ANR详解
引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App ...
随机推荐
- C语言 - 获取系统时间 以年月日时分秒的形式输出
ESP32需要给下位机通过UART发送时间戳,形式是年月日时分秒的十六进制数据包. #include <stdio.h> #include <time.h> int main( ...
- python 手机App数据抓取实战一
前言 当前手机使用成为互联网主流,每天手机App产生大量数据,学习爬虫的人也不能只会爬取网页数据,我们需要学习如何从手机 APP 中获取数据,本文就以豆果美食为例,讲诉爬取手机App的流程 环境准备 ...
- Power BI学习
常见用途: 1.连接数据 2.转换和清洗该数据,以创建数据模型 3.创建视觉对象,如提供数据的可视化表示形式的图表或图形 4.在一个或者多个报表页上创建作为视觉对象集合的报表 5.使用Power BI ...
- jenkins 打安卓包 cpu使用过高处理操作
登录两个控制台 打包开始后 在其中一台机器执行ps -ef |grep jenkins 找到执行打包的主进程复制pid 在另一个终端上执行 top -H -p pid 将最耗cpu的线程id转换为16 ...
- Android 使用自定义Drawable 设置圆角矩形或者圆形图片
转自 Android Drawable 那些不为人知的高效用法 本文出自:[张鸿洋的博客] http://blog.csdn.net/lmj623565791/article/details/437 ...
- MySql翻页查询
分页查询在网页中随处可见,那原理是什么呢?下面简单介绍一下基于MySql数据库的limit实现方法. 首先明确为什么要使用分页查询,因为数据庞大,查询不可能全部显示在页面上,如果全部显示在页面上,也会 ...
- js中undefined的几种情况
1.变量声明且没有赋值: 2.获取对象中不存在的属性时: 3.函数需要实参,但是调用时没有传值,形参是undefined: 4.函数调用没有返回值或者return后没有数据,接收函数返回的变量是und ...
- Python模拟登陆某网教师教育网
本文转载自看雪论坛[作者]rdsnow 不得不说,最近的 Python 蛮火的,我也稍稍了解了下,并试着用 Python 爬取网站上的数据 不过有些数据是要登陆后才能获取的,我们每年都要到某教师教育网 ...
- window service 批处理安装/卸载命令
开启服务 @echo.服务启动...... @echo off @sc create 服务名 binPath= "C:\Users\Administrator\Desktop\win32sr ...
- springload热更新的优缺点
java开发web应用没有.net的方便快捷, 原因是传统开发模式下新增修改代码后要查看效果, 一般要重启应用, 导致浪费了许多无谓的时间,没有.net的高效, 任意更新文件实时生效. 但是有个叫sp ...