在 Android 中,输入事件(例如触摸、按键)从硬件传递到应用程序并最终由应用层消费。整个过程涉及多个系统层次,包括硬件层、Linux 内核、Native 层、Framework 层和应用层。我们将深入解析这一流程,并结合代码逐步了解输入事件的传递。
1. 输入事件的产生与传递
输入事件的产生是从硬件触摸屏开始的。触摸屏等输入设备检测到用户的操作(如触摸、滑动),然后将这些事件传递给 Linux 内核。
- 硬件层(触摸屏等):将物理触摸或按键操作转化为信号。
- 内核层:Linux 内核中的 Input 子系统负责接收这些输入信号并生成相应的事件。
在 Android 系统中,输入事件从硬件传递到应用层的大致流程如下:
触摸屏(硬件层) → Linux 内核(Input子系统) → Native Input System(输入事件解析与分发)
→ Framework(事件管理) → 应用层(事件消费)
2. Linux 内核:输入事件的生成与处理
内核的 Input 子系统接收到输入事件后,将其转化为 input_event
结构。每个输入事件包括三部分:
- type:事件的类型,例如 EV_ABS 表示绝对坐标事件。
- code:事件的具体代码,例如 ABS_X 表示 X 轴坐标。
- value:事件的值,例如坐标值。
代码示例
在 Linux 内核中,输入事件使用以下结构定义:
struct input_event {
struct timeval time; // 事件发生时间
__u16 type; // 事件类型
__u16 code; // 事件代码
__s32 value; // 事件值
};
当触摸屏收到用户的操作时,会产生一系列 input_event
事件,传递到 Linux Input 子系统。然后,内核会将这些事件通过 /dev/input/eventX
文件接口暴露给用户态。
3. Native 层:InputReader 和 InputDispatcher
Android 使用 InputReader
和 InputDispatcher
这两个关键组件来处理输入事件。
- InputReader:从 Linux /dev/input/eventX 接口读取事件,并解析为 Android 系统可以理解的 MotionEvent 或 KeyEvent。
- InputDispatcher:将 InputReader 解析后的事件分发给应用程序的 Window。
在 Android 系统中,InputManagerService
是输入系统的核心服务,它在系统启动时被创建,并负责管理整个输入事件的读取和分发。
InputReader 代码流程
在 InputReader
中,EventHub
类负责打开 /dev/input/eventX
设备文件并读取事件。
void EventHub::openDevice(const char* deviceName) {
// 打开设备文件
int fd = open(deviceName, O_RDWR);
// 将设备文件添加到输入设备列表中
mDevices.push_back(fd);
}
InputReader::loopOnce
是 InputReader
的核心处理函数,它不断从事件队列中读取事件并处理。
void InputReader::loopOnce() {
// 读取事件
processEvents();
// 处理事件
dispatchEvent();
}
InputDispatcher 代码流程
InputDispatcher
使用 dispatchEvent
方法将事件分发到合适的 Window
或 Activity
。
void InputDispatcher::dispatchEvent(const Event& event) {
// 获取目标 Window
sp targetWindow = getTargetWindow(event);
// 将事件发送给目标 Window
targetWindow->sendEvent(event);
}
4. Framework 层:事件分发(WindowManagerService)
在 Framework 层,InputManagerService
将事件传递给 WindowManagerService
,WindowManagerService
负责管理所有窗口的输入焦点,并将事件转发给有焦点的窗口。
代码流程
WindowManagerService
中的 dispatchPointerEvent
方法会根据窗口焦点来分发事件。
public void dispatchPointerEvent(MotionEvent event) {
// 获取焦点窗口
WindowState focusedWindow = getFocusedWindow();
if (focusedWindow != null) {
// 将事件发送到焦点窗口
focusedWindow.sendInputEvent(event);
}
}
5. 应用层:事件消费(View 和 Activity)
最终,事件到达应用层。对于触摸事件,Android 使用 onTouchEvent
方法处理,而对于按键事件,则使用 onKeyDown
、onKeyUp
等方法处理。
示例代码
在 Activity
或 View
中,可以通过重写 onTouchEvent
来消费事件。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 处理按下事件
break;
case MotionEvent.ACTION_MOVE:
// 处理移动事件
break;
case MotionEvent.ACTION_UP:
// 处理抬起事件
break;
}
return super.onTouchEvent(event);
}
总结
- 硬件层:产生输入事件并传递到内核。
- Linux 内核:接收输入信号并转化为 input_event。
- Native 层:InputReader 和 InputDispatcher 解析和分发事件。
- Framework 层:WindowManagerService 负责将事件传递给对应的窗口。
- 应用层:Activity 和 View 接收并消费事件。
这一整套流程保证了从物理输入到应用响应的链路完整性和效率。
参考
Android Input Framework Architecture