系列目录 :第一篇:从硬件到应用的事件旅程 | 第二篇:EventHub --- 原始事件的采集者 | 第三篇:InputReader --- 原始事件到Android事件的转换引擎 | 第四篇:InputDispatcher --- 事件分发与ANR超时机制 | 第五篇:应用侧 --- InputChannel、ViewRootImpl与事件消费
一、InputReader 的位置与职责
EventHub → InputReader → InputDispatcher → APP
RawEvent ▲ NotifyArgs
本篇聚焦
InputReader 的职责是把 EventHub 采集到的 RawEvent 加工成 Android 框架层可理解的 KeyEvent 和 MotionEvent,然后通过 InputListener 接口传递给 InputDispatcher。
这涉及:
- 不同设备类型需要不同的处理策略(键盘 vs 触摸 vs 鼠标)
- 坐标系统需要转换(物理像素 → 逻辑坐标 → 显示坐标)
- 多点触控需要跟踪多个手指的状态
- 按键需要做 Linux 键码 → Android 键码的映射
- 需要考虑屏幕旋转等设备状态
源码位置:
frameworks/native/services/inputflinger/InputReader.cpp
frameworks/native/services/inputflinger/InputReader.h
frameworks/native/services/inputflinger/InputListener.h
本文中所有代码块如未特别标注,均来自 InputReader.cpp。
二、InputReader 的线程模型与主循环
2.1 线程启动
源码路径 :frameworks/native/services/inputflinger/InputReader.cpp
cpp
void InputReader::start() {
mThread = new InputReaderThread(*this);
mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
}
InputReaderThread 不断调用 InputReader::loopOnce():
cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
2.2 主循环 loopOnce()
cpp
void InputReader::loopOnce() {
// 1. 从 EventHub 获取事件(阻塞等待)
size_t count = mEventHub->getEvents(timeout, mEventBuffer, EVENT_BUFFER_SIZE);
{
AutoMutex _l(mLock);
if (count) {
// 2. 逐个处理 RawEvent
processEventsLocked(mEventBuffer, count);
}
// 3. 设备超时检测(如触摸抬起后生成完成信号)
}
// 4. 批量通知 InputDispatcher
mQueuedListener->flush();
}
三、InputReader 的核心数据流
3.1 总体架构
EventHub::getEvents()
│ RawEvent[]
▼
InputReader::processEventsLocked()
│
├── DEVICE_ADDED → addDeviceLocked() → 创建 InputDevice + Mapper
├── DEVICE_REMOVED → removeDeviceLocked()
├── FINISHED_DEVICE_SCAN → 通知配置变更
└── 普通输入事件 → processEventsForDeviceLocked()
│
▼ InputDevice::process()
│
▼ InputMapper::process(rawEvent)
│
▼ NotifyArgs
InputListener::notify*(args)
3.2 关键类关系
InputReader
├── mEventHub (EventHub*)
├── mDevices (KeyedVector<int32_t, InputDevice*>)
│ └── InputDevice
│ ├── mMappers (Vector<InputMapper*>)
│ │ ├── SwitchInputMapper
│ │ ├── KeyboardInputMapper
│ │ ├── CursorInputMapper
│ │ ├── TouchInputMapper
│ │ │ ├── SingleTouchInputMapper
│ │ │ └── MultiTouchInputMapper
│ │ ├── JoystickInputMapper
│ │ └── ExternalStylusInputMapper
│ └── device info (classes, identifier, keyMap, ...)
└── mQueuedListener (QueuedInputListener*)
└── 暂存 NotifyArgs,在 loopOnce() 末尾批量 flush
四、InputDevice:设备的内部表示
4.1 InputDevice 的创建
当 EventHub 上报 DEVICE_ADDED 时,InputReader 调用 addDeviceLocked():
cpp
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
InputDevice* device = new InputDevice(&mContext, deviceId, ...);
// 根据设备类别添加对应的 InputMapper
// 一个设备可能有多个 Mapper(如键盘+轨迹球的复合设备)
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
device->addMapper(new KeyboardInputMapper());
}
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper());
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper());
}
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper());
}
device->configure(...);
device->reset(...);
mDevices.add(deviceId, device);
}
4.2 InputDevice::process()
cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
for (size_t i = 0; i < count; i++) {
const RawEvent* rawEvent = &rawEvents[i];
// 逐个 Mapper 处理,不相关的会被忽略
for (size_t j = 0; j < mMappers.size(); j++) {
mMappers[j]->process(rawEvent);
}
}
}
五、InputMapper 体系详解
5.1 KeyboardInputMapper:键盘事件处理
将按键原始事件转换为 Android KeyEvent:
cpp
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
// 1. 键码映射:Linux scanCode → Android keyCode
int32_t keyCode;
if (getEventHub()->mapKey(getDeviceId(), scanCode,
usageCode, &keyCode, &policyFlags)) {
// 2. 确定动作
bool keyDown = (rawEvent->value != 0);
int32_t action = keyDown ? AKEY_EVENT_ACTION_DOWN
: AKEY_EVENT_ACTION_UP;
// 3. 更新 Meta 状态(Shift、Ctrl、Alt 等修饰键)
if (keyDown) {
newMetaState = addMetaState(oldMetaState, keyCode);
} else {
newMetaState = clearMetaState(oldMetaState, keyCode);
}
// 4. 生成 NotifyKeyArgs → 通知 Dispatcher
NotifyKeyArgs args(..., action, keyCode, scanCode, metaState, ...);
getListener()->notifyKey(&args);
}
break;
}
case EV_SYN:
// 同步事件,刷新状态
break;
case EV_LED:
// 更新 LED 状态(如 Caps Lock 指示灯)
break;
}
}
键码映射 是 KeyboardInputMapper 的核心。EventHub 加载了 .kl 文件,mapKey() 通过 KeyMap 完成查找:
Linux scanCode → KeyLayoutMap → Android keyCode
30 → Generic.kl → AKEYCODE_A (29)
114 → Generic.kl → AKEYCODE_VOLUME_DOWN (25)
5.2 MultiTouchInputMapper:多点触控处理
最复杂的 InputMapper,需要跟踪多个手指、识别多种动作、完成坐标变换。
协议 A vs 协议 B
Linux 多点触控有两种协议:
协议 A(老协议)------简单的多点坐标上报,不跟踪手指 ID:
ABS_MT_POSITION_X 100
ABS_MT_POSITION_Y 200
SYN_MT_REPORT
ABS_MT_POSITION_X 300
ABS_MT_POSITION_Y 400
SYN_MT_REPORT
SYN_REPORT
协议 B(现代协议) ------通过 ABS_MT_TRACKING_ID 跟踪每个触点:
ABS_MT_SLOT 0
ABS_MT_TRACKING_ID 45 ← 手指 #45 按下
ABS_MT_POSITION_X 100
ABS_MT_POSITION_Y 200
ABS_MT_PRESSURE 80
SYN_REPORT
Android 7 主要使用协议 B。
核心流程
cpp
void MultiTouchInputMapper::sync(nsecs_t when) {
// 1. 获取所有触点的快照
for (size_t i = 0; i < mMultiTouchMotionAccumulator.getSlotCount(); i++) {
const Slot* slot = mMultiTouchMotionAccumulator.getSlot(i);
if (slot->isInUse()) {
// trackingId >= 0 → 按下的手指
// trackingId == -1 → 抬起的槽位
rawX = slot->x;
rawY = slot->y;
// ... 记录到 pointerCoords
}
}
// 2. 坐标变换
calculateTransformedXY(rawX, rawY, &transformedX, &transformedY);
// 3. 确定动作类型
int32_t action;
if (pointerCount == 0 && oldPointerCount > 0)
action = AMOTION_EVENT_ACTION_UP; // 全部抬起
else if (pointerCount > 0 && oldPointerCount == 0)
action = AMOTION_EVENT_ACTION_DOWN; // 首次按下
else if (pointerCount > oldPointerCount)
action = AMOTION_EVENT_ACTION_POINTER_DOWN(...); // 新手指按下
else if (pointerCount < oldPointerCount)
action = AMOTION_EVENT_ACTION_POINTER_UP(...); // 某手指抬起
else
action = AMOTION_EVENT_ACTION_MOVE; // 移动
// 4. 生成 NotifyMotionArgs → 通知 Dispatcher
NotifyMotionArgs args(..., action, pointerCount, pointerCoords, ...);
getListener()->notifyMotion(&args);
}
坐标变换
cpp
void TouchInputMapper::calculateTransformedXY(int32_t rawX, int32_t rawY,
float* outX, float* outY) {
float x = rawX, y = rawY;
// 1. 归一化到 [0, 1] 范围
x = (x - rawMinX) / (rawMaxX - rawMinX);
y = (y - rawMinY) / (rawMaxY - rawMinY);
// 2. 应用屏幕旋转(0°/90°/180°/270°)
rotate(x, y, surfaceOrientation);
// 3. 缩放到显示视口
if (viewport) {
*outX = viewportX + x * viewportWidth;
*outY = viewportY + y * viewportHeight;
}
}
5.3 CursorInputMapper:鼠标/轨迹球
处理 EV_REL 类型的相对位移:
cpp
void CursorInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_REL:
if (rawEvent->code == REL_X) mRelX = rawEvent->value;
if (rawEvent->code == REL_Y) mRelY = rawEvent->value;
if (rawEvent->code == REL_WHEEL) mRelWheel = rawEvent->value;
break;
case EV_KEY:
// 鼠标按键(左键 BTN_LEFT、右键 BTN_RIGHT、中键 BTN_MIDDLE)
if (rawEvent->code == BTN_LEFT) ...;
break;
case EV_SYN:
sync(rawEvent->when); // 累积的相对位移一次生成 MotionEvent
break;
}
}
鼠标光标坐标由 InputDispatcher 维护,CursorInputMapper 只提供相对位移量。
六、QueuedInputListener:批量通知机制
InputReader 不会每加工一个事件就通知 InputDispatcher,而是批量暂存,最后一起通知:
cpp
class QueuedInputListener : public InputListenerInterface {
Vector<NotifyArgs> mArgsQueue;
virtual void notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args)); // 暂存
}
virtual void notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args)); // 暂存
}
void flush() {
for (size_t i = 0; i < mArgsQueue.size(); i++) {
mInnerListener->notifyXxx(mArgsQueue[i]); // 批量转发
}
mArgsQueue.clear();
}
};
在 loopOnce() 末尾调用 mQueuedListener->flush() 一次性把本轮所有事件传递给 InputDispatcher。减少跨线程通信开销。
七、InputListener 接口
cpp
class InputListenerInterface {
public:
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs*) = 0;
virtual void notifyKey(const NotifyKeyArgs*) = 0;
virtual void notifyMotion(const NotifyMotionArgs*) = 0;
virtual void notifySwitch(const NotifySwitchArgs*) = 0;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs*) = 0;
};
InputDispatcher 实现了这个接口,notifyKey() 和 notifyMotion() 即分发入口。
八、一个触摸事件的完整加工过程
以用户手指触摸屏幕为例:
RawEvent 序列(从 EventHub 读取):
EV_ABS ABS_MT_TRACKING_ID 45 ← 新手指
EV_ABS ABS_MT_POSITION_X 320
EV_ABS ABS_MT_POSITION_Y 580
EV_ABS ABS_MT_PRESSURE 128
EV_SYN SYN_REPORT ← 一帧数据完毕
InputReader 处理:
1. processEventsLocked()
→ processEventsForDeviceLocked()
→ InputDevice::process()
2. MultiTouchInputMapper 处理每个 RawEvent
slot 0: trackingId=45, x=320, y=580, pressure=128
3. EV_SYN 到达 → sync(when):
a. 检查 trackingId 45 是否是新的 → 是 → 判定为 ACTION_DOWN
b. calculateTransformedXY(320, 580):
- 归一化: x=320/1080=0.296, y=580/1920=0.302
- 旋转: 无旋转
- 映射视口: x=0.296*1080=320, y=0.302*1920=580
c. 生成 NotifyMotionArgs(ACTION_DOWN, pointerCount=1, ...)
4. notifyMotion(&args) → 加入 mArgsQueue
5. loopOnce() 末尾 → flush() → InputDispatcher::notifyMotion()
九、总结
| 组件 | 职责 |
|---|---|
| InputReaderThread | 驱动 loopOnce() 主循环 |
| InputDevice | 代表一个物理设备,管理一组 Mapper |
| KeyboardInputMapper | Linux 键码 → Android 键码映射,Meta 状态管理 |
| MultiTouchInputMapper | 多点触控跟踪,协议 B 解析,动作类型判定 |
| SingleTouchInputMapper | 单点触控,协议 A 兼容 |
| CursorInputMapper | 相对位移累积,鼠标按键处理 |
| QueuedInputListener | 批量通知,减少跨线程通信 |
| 坐标变换 | raw → 归一化 → 旋转 → 视口映射 |
下一篇将聚焦 InputDispatcher,揭开事件分发与 ANR 超时机制的面纱。