input 中的input channel理解

这里是基于 InputChannel 机制的详细时序流程图。

这个图清晰地展示了 SystemServerApp 两个进程是如何通过 Socket Pair (InputChannel) 进行无 Binder 的高效通信的,以及 ANR 监控闭环是如何形成的。

InputChannel 交互流程图

流程图关键点解析:

  1. 颜色区分进程
    • 蓝色区域:系统进程(SystemServer),负责产生和分发事件。
    • 黄色区域:应用进程(App),负责消费事件。
  2. 红色与蓝色箭头(中间的 Socket 传输)
    • 这是 InputChannel 的核心。你可以看到数据直接在 ServerSockClientSock 之间传递,没有经过 Binder
    • ServerSock -> ClientSock (红色):传输具体的 MotionEvent 数据。
    • ClientSock -> ServerSock (蓝色):传输处理完成的信号(ACK),用于告诉系统“我处理完了,不要 ANR 我”。
  3. Looper 的作用
    • App 并没有一直死循环去读 Socket,而是利用 Linux 的 epoll 机制将 Socket FD 挂载到主线程的 MessageQueue 中。
    • 只有当系统侧写入数据时,App 主线程才会被唤醒处理,这样非常省电且高效。
  4. 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,但输入事件比较特殊:

  1. 高频率:手指在屏幕上滑动时,每秒可能产生 60~120 个甚至更多的 MotionEvent。
  2. 低延迟:用户对触摸的反馈极其敏感。
  3. 大吞吐量:输入事件流源源不断。

Binder 的开销相对较大,且 Binder 缓冲区大小有限(通常 1MB 左右,所有进程共享)。如果用 Binder 传递高频触摸事件,容易造成系统卡顿。

Socket (InputChannel)

  • 通信效率更高,适合字节流传输。
  • 可以利用 epoll 机制挂载到 App 主线程的 Looper 上,实现消息驱动。

3. 创建与连接流程

InputChannel 的生命周期与 Window(窗口)紧密绑定。流程如下:

  1. App 请求添加窗口: App 调用 WindowManager.addView() ViewRootImpl.setView() Session.addToDisplay()
  2. WMS 创建通道对WindowManagerService (WMS) 收到请求后,会调用 InputManagerService (IMS) 的接口。 IMS 内部调用 Native 层的 InputChannel::openInputChannelPair,创建两个 socket fd:
    • Server Channel (名为:server)
    • Client Channel (名为:client)
  3. 服务端注册: WMS 将 Server Channel 注册给 InputDispatcher。从此,Dispatcher 知道有一个新窗口可以接收事件了。
  4. 客户端传递: WMS 通过 Binder 将 Client Channel 返回给 App 进程。
  5. App 端接收与监听: App 的 ViewRootImpl 拿到 Client Channel 后,会创建一个 WindowInputEventReceiver。 这个 Receiver 会把 Channel 中的 fd 添加到主线程 LooperMessageQueue 中(利用 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

关键点:双向通信

  1. 去程 (System App):Dispatcher 将 MotionEvent 写入 Socket,App 读取并分发给 View。
  2. 回程 (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 的通信机制中:

  1. Dispatcher 通过 Channel 发送事件给 App。
  2. Dispatcher 记录发送时间,并设置一个 waitQueue
  3. Dispatcher 等待 App 通过 Channel 返回 “Finish” 信号。
  4. 如果 App 主线程卡死(例如 Thread.sleep 或死循环),无法从 Looper 中读取 Channel 数据,或者读取了无法处理完成。
  5. 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