input 中的input channel理解
这里是基于 InputChannel 机制的详细时序流程图。
这个图清晰地展示了 SystemServer 和 App 两个进程是如何通过 Socket Pair (InputChannel) 进行无 Binder 的高效通信的,以及 ANR 监控闭环是如何形成的。
InputChannel 交互流程图

流程图关键点解析:
- 颜色区分进程:
- 蓝色区域:系统进程(SystemServer),负责产生和分发事件。
- 黄色区域:应用进程(App),负责消费事件。
- 红色与蓝色箭头(中间的 Socket 传输):
- 这是 InputChannel 的核心。你可以看到数据直接在
ServerSock和ClientSock之间传递,没有经过 Binder。 ServerSock -> ClientSock(红色):传输具体的MotionEvent数据。ClientSock -> ServerSock(蓝色):传输处理完成的信号(ACK),用于告诉系统“我处理完了,不要 ANR 我”。
- 这是 InputChannel 的核心。你可以看到数据直接在
- Looper 的作用:
- App 并没有一直死循环去读 Socket,而是利用 Linux 的
epoll机制将 Socket FD 挂载到主线程的MessageQueue中。 - 只有当系统侧写入数据时,App 主线程才会被唤醒处理,这样非常省电且高效。
- App 并没有一直死循环去读 Socket,而是利用 Linux 的
- ANR 闭环:
- 注意步骤 7(启动计时)和步骤 15(移除计时)。
- 如果 App 在步骤 11(View处理)卡住了(比如在
onTouch里睡了 10 秒),步骤 13 的 ACK 就永远发不出去,Dispatcher 那边的计时器就会超时,导致 ANR。
在 Android 的 Input 系统中,InputChannel(输入通道) 是一个非常核心的概念。简单来说,它是SystemServer 进程(系统)与 App 进程(应用)之间传递输入事件的“专用管道”。
以下是关于 InputChannel 的深度解析,从本质、创建流程、工作原理到它的作用。
1. 本质是什么?
从操作系统的角度看,InputChannel 的本质是一个 Unix Domain Socket Pair(套接字对)。
- 它包含两个 文件描述符 (File Descriptors, FDs)。
- 这是一个全双工的通信管道。
- 服务端 (Server 端):保留在
SystemServer进程中,由InputDispatcher持有。 - 客户端 (Client 端):通过 Binder 传递给 App 进程,由
ViewRootImpl持有。
一句话总结:InputChannel 就是一条连接“输入系统”和“应用窗口”的高速公路,专门用来运输触摸(Touch)、按键(Key)等事件。

2. 为什么需要 InputChannel?(为什么不用 Binder?)
虽然 Android 中大部分跨进程通信(IPC)都使用 Binder,但输入事件比较特殊:
- 高频率:手指在屏幕上滑动时,每秒可能产生 60~120 个甚至更多的 MotionEvent。
- 低延迟:用户对触摸的反馈极其敏感。
- 大吞吐量:输入事件流源源不断。
Binder 的开销相对较大,且 Binder 缓冲区大小有限(通常 1MB 左右,所有进程共享)。如果用 Binder 传递高频触摸事件,容易造成系统卡顿。
而 Socket (InputChannel):
- 通信效率更高,适合字节流传输。
- 可以利用
epoll机制挂载到 App 主线程的Looper上,实现消息驱动。
3. 创建与连接流程
InputChannel 的生命周期与 Window(窗口)紧密绑定。流程如下:
- App 请求添加窗口:
App 调用
WindowManager.addView()→ViewRootImpl.setView()→Session.addToDisplay()。 - WMS 创建通道对:
WindowManagerService(WMS) 收到请求后,会调用InputManagerService(IMS) 的接口。 IMS 内部调用 Native 层的InputChannel::openInputChannelPair,创建两个 socket fd:Server Channel(名为:server)Client Channel(名为:client)
- 服务端注册:
WMS 将
Server Channel注册给InputDispatcher。从此,Dispatcher 知道有一个新窗口可以接收事件了。 - 客户端传递:
WMS 通过 Binder 将
Client Channel返回给 App 进程。 - App 端接收与监听:
App 的
ViewRootImpl拿到Client Channel后,会创建一个WindowInputEventReceiver。 这个 Receiver 会把 Channel 中的 fd 添加到主线程Looper的MessageQueue中(利用 native 的 epoll 机制)。
结果:一旦 Socket 有数据(事件)写入,App 的主线程 Looper 就会被唤醒,回调 onInputEvent 进行处理。
4. 通信流程图解
flowchart TD subgraph System_Server进程 A[InputReader] -->|读取底层事件| B[InputDispatcher] B -->|1. 写入事件| C{Server Channel<br>Socket FD} end C <== socket通信 ==> D{Client Channel<br>Socket FD} subgraph App进程 D -->|2. Looper唤醒| E[InputEventReceiver] E -->|3. 分发| F[ViewRootImpl] F -->|4. 处理| G[DecorView / Activity] G -->|5. 处理完成| F F -->|6. 发送finish信号| D end D -->|7. 反馈ACK| C C -->|8. 清理队列/ANR检测| B
关键点:双向通信
- 去程 (System → App):Dispatcher 将
MotionEvent写入 Socket,App 读取并分发给 View。 - 回程 (App → System):App 处理完事件后(
onTouchEvent返回),必须通过 Channel 发送一个 “Finished” 信号 (ACK) 给 System。- 注意:如果 System 收不到这个 ACK,就会认为 App 卡住了,超时后触发 ANR (Input dispatching timed out)。
5. 关键类与代码对应
- Java 层:
android.view.InputChannel:Java 层的封装类,持有一个 Native 指针。android.view.InputEventReceiver:App 端接收事件的基类。android.view.ViewRootImpl.WindowInputEventReceiver:具体的实现类,负责将事件分发给 View 树。
- Native 层 (C++):
InputChannel.cpp:封装了 socket 的读写操作。InputPublisher(在 System 端):负责把事件序列化并写入 socket。InputConsumer(在 App 端):负责从 socket 读取数据并反序列化为事件对象。
6. 常见问题:InputChannel 与 ANR
Input ANR 的核心判定逻辑就在 InputChannel 的通信机制中:
- Dispatcher 通过 Channel 发送事件给 App。
- Dispatcher 记录发送时间,并设置一个
waitQueue。 - Dispatcher 等待 App 通过 Channel 返回 “Finish” 信号。
- 如果 App 主线程卡死(例如
Thread.sleep或死循环),无法从 Looper 中读取 Channel 数据,或者读取了无法处理完成。 - Dispatcher 等待超时(默认 5秒),判定该窗口无响应,弹出 ANR 对话框。
总结
- InputChannel 是管道:基于 Socket Pair,连接 SystemServer 和 App。
- InputChannel 是绑定窗口的:每个
WindowState(系统侧) 对应一个ViewRootImpl(应用侧),它们之间就有一对 InputChannel。 - InputChannel 决定了流畅度:它避开了 Binder,保证了高频触摸事件的高效传递。
@startuml
skinparam dpi 150
skinparam responseMessageBelowArrow true
skinparam ParticipantPadding 20
skinparam BoxPadding 10
title Android InputChannel 通信机制原理图
actor User as "用户操作\n(Touch/Key)"
box "System_Server 进程" #E6F2FF
participant Reader as "InputReader"
participant Dispatcher as "InputDispatcher"
participant WMS as "WindowManager\nService"
participant ServerSock as "Server Channel\n(Socket FD)"
endbox
box "App 进程" #FFF9E6
participant ClientSock as "Client Channel\n(Socket FD)"
participant Looper as "Main Looper\n(Native MessageQueue)"
participant Receiver as "WindowInputEvent\nReceiver"
participant VRI as "ViewRootImpl"
participant View as "View/Activity\n(UI Handler)"
endbox
== 第一阶段:通道建立 (初始化) ==
VRI -> WMS: 1. addToDisplay (请求添加窗口)
activate WMS
WMS -> WMS: 2. InputChannel.openInputChannelPair()
note right
创建 Unix Domain Socket Pair
生成两个文件描述符 (fd0, fd1)
end note
WMS -> Dispatcher: 3. 注册 Server Channel (fd0)
note left: Dispatcher 此时持有\n发送端的 socket
WMS -> VRI: 4. 通过 Binder 返回 Client Channel (fd1)
deactivate WMS
activate VRI
VRI -> Receiver: 5. 创建 Receiver 封装 Client Channel
Receiver -> Looper: 6. addFd (将 socket fd 加入 epoll 监听)
deactivate VRI
note right: App 主线程开始监听 socket 可读事件
== 第二阶段:事件分发 (System -> App) ==
User -> Reader: 硬件中断/事件
Reader -> Dispatcher: 转换成 MotionEvent
activate Dispatcher
Dispatcher -> Dispatcher: 7. 寻找目标窗口 (Focused Window)
Dispatcher -> Dispatcher: 启动 ANR 计时器 (WaitQueue)
Dispatcher -> ServerSock: 8. InputPublisher.publish()
note right: 序列化事件数据写入 Socket
ServerSock -[#red]> ClientSock: **Socket 传输 (字节流)**
note right: 避开 Binder,全双工管道
ClientSock -> Looper: 9. Socket 有数据 -> 唤醒主线程 (epoll_wait返回)
Looper -> Receiver: 10. native回调 dispatchInputEvent
activate Receiver
Receiver -> ClientSock: InputConsumer.consume() (读取数据)
== 第三阶段:应用处理 (In App) ==
Receiver -> VRI: enqueueInputEvent
VRI -> View: 11. deliverInputEvent (分发给 View 树)
activate View
View -> View: onTouchEvent / onClick
View --> VRI: return handled (true/false)
deactivate View
== 第四阶段:反馈与 ANR 消除 (App -> System) ==
VRI -> Receiver: 12. finishInputEvent (处理完毕)
Receiver -> ClientSock: 13. 发送 Finish 信号 (ACK)
deactivate Receiver
ClientSock -[#blue]> ServerSock: **Socket 传输 (ACK 信号)**
ServerSock -> Dispatcher: 14. 收到 ACK
Dispatcher -> Dispatcher: 15. 从 WaitQueue 移除事件
note left
移除成功 -> 重置 ANR 计时
移除失败(超时) -> 触发 "Input dispatching timed out"
end note
deactivate Dispatcher
@enduml