Framework 补完计划 (4):Input 系统全链路 —— 从触摸电信号到 Compose 的 PointerInput

20 阅读2分钟

前言

在 Compose 中,处理点击只需一个 Modifier.clickable。但在底层,从你的手指触碰屏幕,到 Kotlin 函数被唤醒,经历了一场横跨内核态与用户态的长途跋涉。

今天我们拆解这一复杂的“神经反射弧”。


一、 起点:内核驱动(Kernel Space)

当你触碰屏幕:

  1. 产生中断:硬件产生一个电信号中断。
  2. 生成事件:内核驱动将其封装为 input 事件,写入 /dev/input/eventX 设备文件。

二、 传递:InputReader 与 InputDispatcher

在系统的 system_server 进程中,有两个英雄在不停忙碌:

  1. InputReader:运行在独立线程。它不断轮询设备文件,读取原始坐标,并将其标准化。
  2. InputDispatcher:它是路由中心。它需要查 WMS(WindowManager):
    • “现在哪个窗口在最上面?”
    • “这个点落在了哪个窗口范围内?” 然后通过 LocalSocket (InputChannel) 将事件跨进程发给 App 进程。

三、 到达:App 进程的 ViewRootImpl

当事件进入 App 进程:

  1. Looper 唤醒:主线程被 Socket 信号唤醒。
  2. 分发逻辑:事件流向 ViewRootImpl -> DecorView -> 最后到达我们的 AndroidComposeView

四、 Compose 内部的“二次演译”

一旦进入 AndroidComposeView,传统的 onTouchEvent 就结束了。

  1. Hit Testing:Compose 开始在自己的 LayoutNode 树上进行坐标比对。
  2. PointerInput 链:事件被封装为 PointerInputChange
  3. 消费控制:Compose 通过 Pass(初测、主测、终测)三轮分发,让子组件和父组件能精细地控制“拦截”和“消费”。

五、 给十年老兵的架构挑战

  1. 响应延迟优化:理解了全链路,你就会明白为什么主线程卡顿(由于刚才说的 Choreographer 问题)会导致点击没反应。
  2. 跨端手势统一:在 iOS 上,底层事件来自 UITouch。KMP 通过 compose-ui 将这些不同的原生事件抽象为统一的 PointerInput,这正是我们在全栈项目中实现“一套代码处理手势”的底层保障。

六、 源码探秘路径

  • frameworks/native/services/inputflinger/:Input 系统的 C++ 核心。
  • android.view.InputEventReceiver:Java 层的接收端。
  • androidx.compose.ui.input.pointer.PointerInputEvent:Compose 的封装入口。

结语

手感即正义。理解了 Input 链路,你就能解决最棘手的“交互冲突”和“响应迟钝”问题。

下一篇,我们将探讨线程间通信的灵魂 —— Looper、Handler 与 Epoll:Android 异步设计的本质