Android Input系统(1) Input事件的产生与传递

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Android Input系统(1) Input事件的产生与传递

这篇博客介绍了Android系统中的Input系统,它是负责处理用户输入操作的核心组件。该系统从各种输入设备获取原始输入事件,并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。博客详细解释了Input系统的角色和重要性,以及它的主要组成部分,包括InputReader、InputDispatcher等。同时,还介绍了输入事件的来源,包括硬件设备和软件模拟,以及不同类型的输入事件,如按键事件和触摸事件。

Android Input系统简介

  • Input系统的角色和重要性
  • Input系统是Android系统中负责处理用户输入操作的核心组件,它负责从各种输入设备(如屏幕、键盘、鼠标等)获取原始的输入事件(如按键、触摸、滑动等),并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。
  • Input系统对于提供流畅、灵敏和一致的用户交互体验至关重要,它需要在不同的设备、场景和应用中保证输入事件的正确性、及时性和安全性。
  • Input系统的主要组成部分
  • Input系统由以下几个主要部分组成:
组件 功能 位置
./native/services/inputflinger/reader/EventHub.cpp 监听/dev/input目录下的输入设备节点,获取内核事件 Native层
./native/services/inputflinger/reader/InputReader.cpp 从EventHub读取内核事件,进行预处理和分类,生成InputEvent对象 Native层
./native/services/inputflinger/dispatcher/InputDispatcher.cpp 从InputReader接收InputEvent对象,进行策略判断和分发,将事件发送给目标窗口或应用 Native层
./base/services/core/java/com/android/server/input/InputManagerService.java 提供Binder服务接口,管理InputReader和InputDispatcher,处理来自应用层或策略层的请求或回调 Java层
./base/core/java/android/hardware/input/InputManager.java 提供Java API接口,供应用层或策略层调用InputManagerService Java层

Input事件的来源

硬件设备

  • 硬件设备是最常见的输入事件来源,例如触摸屏、物理键盘、鼠标、游戏手柄等。这些设备通过驱动程序将用户操作转换为内核事件,并写入到/dev/input目录下对应的设备节点中。
  • EventHub会监听这些设备节点,并通过getevent工具读取内核事件。内核事件包含了事件类型(如EV_KEY, EV_ABS等)、事件代码(如KEY_A, ABS_X等)和事件值(如0, 1等)。
软件模拟
  • 软件模拟是另一种输入事件来源,例如虚拟键盘、语音输入、手势识别等。这些软件通过sendevent工具或InputManagerService的injectInputEvent方法向/dev/input目录下的设备节点写入内核事件。
  • EventHub也会监听这些设备节点,并通过getevent工具读取内核事件。内核事件的格式和硬件设备产生的一样,只是事件源(source)不同。

截图中的2个 第一部分是由adb 发送了input keyevent 224 , 第二部分是由鼠标点击了左键1次。

Event Type deviceId source action keyCode scanCode buttonState xCursorPosition yCursorPosition policyFlags age
KeyEvent -1 0x00000000 DOWN 224 0 - - - 0x2b000000 5205ms
KeyEvent -1 0x00000000 UP 224 0 - - - 0x2b000000 5205ms
MotionEvent 4 0x00002002 DOWN - - 0x00000001 708.6 586.5 0x62000001 1118ms
MotionEvent 4 0x00002002 UNKNOWN - - 0x00000001 708.6 586.5 0x62000001 1118ms
MotionEvent 4 0x00002002 UNKNOWN - - 0x00000000 708.6 586.5 0x62000000 974ms
MotionEvent 4 0x00002002 UP - - 0x00000000 708.6 586.5 0x62000000 974ms

详细解释下:

  1. Event Type: 表示事件的类型,可以是按键事件(KeyEvent)或触摸事件(MotionEvent)。在提供的日志中,既包含了按键事件又包含了触摸事件。
  2. deviceId: 表示输入设备的ID,每个输入设备都有一个唯一的ID。在日志中,deviceId为-1表示未指定或不适用。
  3. source: 表示事件的来源或输入设备类型。它是一个位掩码,指示事件来自哪种类型的输入设备。在提供的日志中,source的值采用16进制表示,例如0x00000000和0x00002002。
  4. action: 表示事件的动作,即事件的状态或操作类型。对于按键事件,动作可以是DOWN(按下)或UP(释放)。对于触摸事件,动作可以是DOWN(按下)、UP(释放)或其他(如UNKNOWN)。
  5. keyCode: 表示按键事件中按下或释放的按键的唯一标识符。它与具体硬件设备无关,是Android系统定义的一套统一的按键编码。
  6. scanCode: 表示内核事件中用来表示按键的唯一标识符,它与硬件设备相关,不同的设备可能有不同的scanCode。
  7. buttonState: 表示触摸事件中的鼠标按钮状态。在提供的日志中,buttonState为0x00000000表示没有按下鼠标按钮,为0x00000001表示按下了鼠标左键。
  8. xCursorPosition: 表示触摸事件或鼠标事件发生时的X轴坐标位置。
  9. yCursorPosition: 表示触摸事件或鼠标事件发生时的Y轴坐标位置。
  10. policyFlags: 表示事件的策略标志或属性,它是一个位掩码,表示事件相关的特征或行为。
  11. age: 表示事件生成的时间或年龄,单位为毫秒。它表示事件生成时间与当前时间的间隔。

Input事件的类型

  • 按键事件(KeyEvent)
  • 按键事件是指用户按下或松开一个按键时产生的事件,例如物理键盘上的字母键、数字键、功能键等,或者虚拟键盘上的返回键、菜单键、音量键等。
  • 按键事件由KeyEvent类表示,它包含了按键的状态(如ACTION_DOWN, ACTION_UP等)、代码(如KEYCODE_A, KEYCODE_BACK等)、标志(如FLAG_CANCELED, FLAG_LONG_PRESS等)、源(如SOURCE_KEYBOARD, SOURCE_GAMEPAD等)、设备(如Keyboard, Mouse等)等属性。
  • 触摸事件(MotionEvent)
  • 触摸事件是指用户在触摸屏上进行触摸、滑动、缩放等操作时产生的事件,例如单点触摸、多点触摸、手势识别等。
  • 触摸事件由MotionEvent类表示,它包含了触摸的状态(如ACTION_DOWN, ACTION_MOVE等)、位置(如X, Y坐标)、压力(如pressure, size等)、速度(如xVelocity, yVelocity等)、源(如SOURCE_TOUCHSCREEN, SOURCE_STYLUS等)、设备(如Touchscreen, Stylus等)等属性。
  • 其他事件(GenericMotionEvent)

  • 其他事件是指用户使用非触摸屏的输入设备进行移动或旋转操作时产生的事件,例如鼠标移动、滚轮滚动、操纵杆移动、陀螺仪旋转等。
  • 其他事件也由MotionEvent类表示,但是它们的状态是ACTION_GENERIC_MOTION,而且它们的属性可能和触摸事件不同,例如轴值(如AXIS_X, AXIS_Y等)、旋转角度(如AXIS_ORIENTATION, AXIS_TILT等)、源(如SOURCE_MOUSE, SOURCE_JOYSTICK等)、设备(如Mouse, Joystick等)等。

Input事件的属性

  • 事件代码(scanCode和keyCode)
  • scanCode是内核事件中用来表示按键的唯一标识符,它与硬件设备相关,不同的设备可能有不同的scanCode。
  • keyCode是KeyEvent中用来表示按键的唯一标识符,它与硬件设备无关,是Android系统定义的一套统一的按键编码。
  • scanCode和keyCode之间有一个映射关系,由KeyLayout文件定义。KeyLayout文件是一种文本文件,用来描述输入设备的按键布局和功能。Android系统会根据输入设备的名称或ID加载对应的KeyLayout文件,并根据文件中的映射规则将scanCode转换为keyCode。
  • 事件标志(flags)
  • flags是KeyEvent或MotionEvent中用来表示事件特征或行为的位掩码,它可以有多个值同时存在,例如FLAG_CANCELED表示事件被取消,FLAG_LONG_PRESS表示长按事件,FLAG_FROM_SYSTEM表示系统产生的事件等 。
  • flags可以影响事件的分发和处理过程,例如FLAG_CANCELED会导致后续的ACTION_UP或ACTION_MOVE被忽略,FLAG_FROM_SYSTEM会导致某些策略层拦截或修改事件等。
  • 事件源(source)
  • source是KeyEvent或MotionEvent中用来表示输入设备类型或类别的位掩码,它可以有多个值同时存在,例如SOURCE_KEYBOARD表示来自物理键盘的事件,SOURCE_TOUCHSCREEN表示来自触摸屏。

Input事件的流程

  • InputReader
  • InputReader是Input系统中负责从EventHub读取内核事件,并将其转换为InputEvent对象的组件。它运行在一个单独的线程(InputReaderThread)中,不断地调用EventHub的getEvents方法获取内核事件。
  • InputReader会根据内核事件的类型(EV_KEY, EV_ABS等)和设备节点(event0, event1等)进行分类和预处理,例如根据KeyLayout文件将scanCode转换为keyCode,根据TouchScreenCalibration文件将触摸坐标进行校准,根据InputDeviceConfiguration文件将设备信息进行解析等。
  • InputReader还会根据内核事件的属性(source, device等)和策略层(InputReaderPolicy)的建议,将内核事件转换为KeyEvent或MotionEvent对象,并赋予相应的状态(action, flags等)和时间戳(eventTime, downTime等)。
  • InputReader最后会将生成的InputEvent对象发送给InputClassifier,进行进一步的处理。
  • InputDispatcher
  • InputDispatcher是Input系统中负责从InputClassifier接收InputEvent对象,并将其分发给目标窗口或应用的组件。它运行在另一个单独的线程(InputDispatcherThread)中,不断地调用InputClassifier的process方法获取InputEvent对象。
  • InputDispatcher会根据InputEvent对象的类型(KeyEvent或MotionEvent)、属性(flags, source等)和策略层(InputDispatcherPolicy)的建议,进行策略判断和分发,例如判断是否需要拦截或修改事件,判断是否需要唤醒设备或关闭屏幕,判断是否需要显示或隐藏输入法等。
  • InputDispatcher还会根据窗口管理层(WindowManagerService)提供的窗口信息(WindowState, LayoutParams等),找到合适的目标窗口或应用,并通过InputChannel与之通信,将事件发送给目标窗口或应用。
  • InputDispatcher最后会等待目标窗口或应用对事件的消费结果(如Consumed, Injected等),并根据结果进行相应的处理,例如回调策略层或窗口管理层,更新事件状态或队列等。
  • InputChannel
  • InputChannel是一种用于在进程间传递InputEvent对象的通信机制,它是基于Binder机制实现的。每个InputChannel都包含一对文件描述符(fd),分别用于读取和写入InputEvent对象。
  • InputChannel可以在不同的进程间传递,例如InputDispatcher可以将一个InputChannel传递给目标窗口或应用所在的进程,然后通过这个InputChannel向目标窗口或应用发送InputEvent对象。
  • InputChannel还可以在同一个进程内传递,例如目标窗口或应用可以将一个InputChannel传递给它自己的子线程或子组件,然后通过这个InputChannel向子线程或子组件发送InputEvent对象。

参考:

创建输入法 | Android 开发者 | Android Developers

Android 设备输入事件(input)派发原理总结 - 掘金

Android Input架构 - 简书

还没写完 , 有空继续…

有什么问题 欢迎留言~

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
88 2
|
30天前
|
JavaScript 前端开发 Java
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
40 0
|
4天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
1天前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
|
24天前
|
Android开发 UED 开发者
Android经典实战之WindowManager和创建系统悬浮窗
本文详细介绍了Android系统服务`WindowManager`,包括其主要功能和工作原理,并提供了创建系统悬浮窗的完整步骤。通过示例代码,展示了如何添加权限、请求权限、实现悬浮窗口及最佳实践,帮助开发者轻松掌握悬浮窗开发技巧。
49 1
|
1月前
|
Java 物联网 Android开发
移动应用与系统:技术演进与未来展望探索安卓应用开发:从新手到专家的旅程
【8月更文挑战第28天】本文将深入探讨移动应用开发的技术演进、移动操作系统的发展历程以及未来的发展趋势。我们将通过实例和代码示例,展示如何利用最新的技术和工具来开发高效、可靠的移动应用。无论你是初学者还是经验丰富的开发者,这篇文章都将为你提供有价值的信息和见解。 【8月更文挑战第28天】在这个数字时代,掌握安卓应用的开发技能不仅是技术人员的追求,也成为了许多人实现创意和梦想的途径。本文将通过深入浅出的方式,带领读者从零基础开始,一步步走进安卓开发的奇妙世界。我们将探讨如何配置开发环境,理解安卓应用的核心组件,以及如何通过实际编码来构建一个功能完整的应用。无论你是编程新手还是希望提升自己的开发者
|
1月前
|
存储 安全 物联网
Android经典实战之跳转到系统设置页面或其他系统应用页面大全
本文首发于公众号“AntDream”,关注获取更多技巧。文章总结了Android开发中跳转至系统设置页面的方法,包括设备信息、Wi-Fi、显示与声音设置等,并涉及应用详情与电池优化页面。通过简单的Intent动作即可实现,需注意权限与版本兼容性。每日进步,尽在“AntDream”。
159 2
|
1月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
29天前
|
安全 Android开发 iOS开发
安卓与iOS的终极对决:哪个系统更适合你?
在智能手机的世界里,安卓和iOS两大操作系统如同两座巍峨的山峰,各自拥有庞大的用户群体。本文将深入浅出地探讨这两个系统的优缺点,并帮助你找到最适合自己的那一款。让我们一起揭开这场技术盛宴的序幕吧!
|
1月前
|
开发工具 Android开发
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
30 0