InputChannel 与 InputEventReceiver:应用如何收到事件
InputChannel 与 InputEventReceiver:应用如何收到事件
上一章里 dispatcher 找到了目标窗口。这一章回答:事件怎么物理送到应用进程,应用又在 哪个线程、以什么方式接收?
窗口添加过程中,系统建立一对 InputChannel:服务端一端注册给 InputDispatcher,客户端 一端交给应用。应用把通道接入 Looper,由 InputEventReceiver 在可读时取出事件。
收到事件不等于消费完成
| 常见误解 | 正确理解 |
|---|---|
| dispatcher 直接调用应用的 View | 它把事件写进 InputChannel,应用通过 InputEventReceiver 自己取 |
| 应用收到事件就“完成”了 | 必须调用 finishInputEvent() 才表示消费完成,否则可能触发 ANR |
| 每个窗口一个通道,由应用自己创建 | 通道由 IMS 创建通道对,服务端注册给 dispatcher,客户端随窗口返回给应用 |
事件进入应用后还要经过 ViewRootImpl 的输入阶段和 View 树。处理结束必须通过 finishInputEvent() 回告系统;只完成读取、没有完成回执,dispatcher 仍会把该事件视为未完成。
核心结论
- 窗口添加时,系统创建一对 InputChannel:服务端注册进 dispatcher,客户端随窗口返回应用。
- 应用进程里,
InputEventReceiver挂在 Looper 上,在自己的线程收到事件回调。 ViewRootImpl通过InputStage把接收事件送入 View 分发链。- 每个事件处理完必须
finishInputEvent(),dispatcher 以此判断消费完成并开始/停止 ANR 计时。 - 接收、分发、消费都在应用进程完成;dispatcher 只负责投递与计时。
从注册到接收
逐步解释:
- 创建通道对:窗口添加时由系统创建一对 InputChannel。
- 两侧分工:服务端给 dispatcher 投递用,客户端给应用接收用。
- 应用挂载接收器:
InputEventReceiver绑定窗口 Looper,事件到达时回调。 - 进入 View 分发:
ViewRootImpl用 InputStage 链处理事件。 - 消费回执:
finishInputEvent()通知 dispatcher,未按时完成则触发 ANR。
为什么要“消费”
dispatcher 无法知道应用业务逻辑是否结束,只能靠 finishInputEvent() 判断。因此从事件 写入通道开始计时,到应用消费回执为止,超过阈值就报输入 ANR。这也解释了为什么 ANR 的 根因既可能是主线程卡死(无法消费),也可能是应用根本没接收(通道或 Looper 问题)。
源码证据
阅读目标:确认通道创建与接收循环入口。
正文指针:InputTransport.cpp nativeCreateInputChannel()(通道对创建); InputEventReceiver.java nativeInit()/dispatchInputEvent()/finishInputEvent(); InputManagerService.java 中通道注册的 Native 桥接。
// InputEventReceiver.java(伪代码,行号以 r75 为准)
public void onInputEvent(InputEvent event) {
// 子类(ViewRootImpl 内部)处理事件
}
public final void finishInputEvent(InputEvent event, boolean handled) {
// 通知 dispatcher 消费完成
}这段代码证明: 接收与回执是应用侧的显式动作;finishInputEvent() 是 dispatcher 知道“这个事件处理完了”的唯一依据。
验证与排障
环境:开发设备或模拟器;以下命令不需要 root。
adb shell dumpsys input | sed -n '/Registered input channels/,$p'
adb logcat -v threadtime | grep -E "InputEventReceiver|InputDispatcher"Registered input channels 段落列出已注册的通道与连接;logcat 可以观察应用侧接收。 如果 dispatcher 已投递但应用没有日志,检查通道是否注册、应用 Looper 是否活着、主线程 是否卡死。
常见误区
- “收到事件就自动算完成”:必须
finishInputEvent()。 - “通道由应用创建”:系统创建通道对,应用只拿客户端。
- “接收发生在任意线程”:接收器挂在窗口的 Looper 上,通常是主线程。
- “dispatcher 能看到业务处理结果”:它只看有没有消费回执,不知道业务逻辑。
延伸问题
InputChannel的dup()与通道复用在多窗口场景怎么用?- 批量接收(batching)对性能有什么影响?
- 应用进程死亡后,dispatcher 如何发现通道断开?
- 系统手势监视器(monitor)与应用窗口共享什么通道机制?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
InputTransport.cpp | nativeCreateInputChannel() | 创建通道对 |
InputEventReceiver.java | nativeInit() / onInputEvent() | 应用侧接收 |
InputEventReceiver.java | finishInputEvent() | 消费回执 |
ViewRootImpl.java | InputStage / deliverInputEvent() | 进入 View 分发链 |
公共路径:frameworks/base/core/java/android/view/ 与 frameworks/native/libs/input/。行号以 r75 检索为准。
dispatcher 使用的窗口状态从何而来,见 输入焦点与触摸目标。