一句话总结
MotionEvent 就像你点的外卖订单,从手指碰屏幕(下单)开始,经过骑手(硬件驱动)、平台(系统服务)层层传递,最终送到你手里(App)处理!
一、MotionEvent的诞生:从硬件到系统服务
MotionEvent 的诞生是一个从物理信号到软件事件的复杂过程。
-
硬件层:当用户触摸屏幕时,触控传感器会产生电信号。这些信号被硬件驱动接收,并转换为内核可识别的原始输入事件。
-
系统服务层:
InputManagerService是 Android 事件处理的核心服务。InputReader:InputReader不断从硬件驱动中读取原始事件,并将其加工成包含坐标、动作类型(ACTION_DOWN、ACTION_MOVE、ACTION_UP)等信息的MotionEvent对象。InputDispatcher:InputDispatcher是事件分发的调度员。它根据MotionEvent的坐标和当前窗口的层级,找到最上层的、有焦点的窗口,并准备将其分发。
二、事件的跨进程分发:从系统到应用
MotionEvent 的传递是一个跨越进程的旅程。InputDispatcher 通过 Binder IPC 将事件发送到目标应用的进程。
- Binder 线程:事件到达应用进程后,首先被 App 的 Binder 线程接收。
- 主线程:Binder 线程随后将
MotionEvent封装成一个Message,并将其发送到主线程的MessageQueue。 Looper:主线程的Looper从MessageQueue中取出Message,并将其分发给Activity的Window。
三、事件分发在App内部的旅程
MotionEvent 到达 App 后,会沿着一条由 Activity、ViewGroup 和 View 组成的责任链进行传递。
-
Activity:Activity的dispatchTouchEvent()是事件分发的起点。 -
ViewGroup:ViewGroup是事件分发的核心。它的onInterceptTouchEvent()方法决定了是否要拦截事件。- 返回
true:ViewGroup拦截事件,并开始调用自己的onTouchEvent()。 - 返回
false:ViewGroup不拦截,事件继续传递给子View。
- 返回
-
View:View是事件分发链的终点。其onTouchEvent()方法的返回值(true或false)决定了该View是否消费了事件。
四、常见问题与解决
1. 为什么点击没反应?
- 原因:可能因为父
ViewGroup拦截了事件,或者View的onTouchEvent在处理ACTION_DOWN时返回了false。 - 解决:检查
ViewGroup的onInterceptTouchEvent,确保其在不需要拦截时返回false。
2. 滑动冲突怎么解决?
- 原因:父
ViewGroup(如ViewPager)和子View(如RecyclerView)都想处理滑动事件,导致冲突。 - 解决:在父
ViewGroup的onInterceptTouchEvent中,根据滑动方向和业务逻辑,动态地返回true或false。
3. ANR的发生
- 如果主线程在 5 秒内没有处理完一个
MotionEvent,InputDispatcher会认为应用无响应,并触发 ANR。 - 解决:将所有耗时操作移到子线程,确保主线程始终保持流畅。