Android 浅析 消息事件分发机制

前言

Linus Benedict Torvalds : RTFSC – Read The Funning Source Code

概述

事件系统分为四部分:采集信息、前期处理、WMS分配、应用程序处理。

  • 采集信息:就是硬件部分收集点击的事件。
  • 前期处理:对刚收集的事件进行格式处理。
  • WMS 分配:WMS 记录了当前系统所有窗口完整状态信息,所以可以判断事件投递的具体进程。
  • 应用程序处理:最后派发给应用程序让我们自己来处理。

采集信息

采集信息基本都是硬件部分的事,无论对屏幕的触碰事件还是键盘的点击事件都是由手机硬件采集,然后保存在本地的一个事件文件里面。

前期处理

在采集完信息后要做最初的事件处理,例如分类:Home、Back、Menu等。事件类型:拖动、点击、其它等。

WMS 分配事件

Android系统中负责管理输入事件的主要是InputManagerService(IMS)。它主要的任务就是从设备中读事件数据,然后将输入事件发送到焦点窗口中去,另外还需要让系统有机会来处理一些系统按键。

InputManagerService里包含了两个非常重要的工作线程。

InputReaderThread

一个独立的循环线程,主要任务就是不断地轮询相关设备节点查看是否有新的事件发生。并将事件告知派发系统。

InputDispatcherThread

一个独立的线程,用以处理从事件轮询系统告知过来的事件,保证事件的正确派发和处理。

在初始化的最初会以参数的形式将InputDispatcherThread的对象封装进InputReaderThread里,在InputReader的loopOnce循环里会不断通过Dispatcher将事件发送给Listener。

InputDispatcherThread 的一个核心工作就是确定事件的接收对象。

在WMS 和 InputDispatcher 的中间存在着一个InputMonitor对象,这个对象作为中介,工作主要分为两部分,第一:实现WindowManagerCallbacks接口,WindowManagerCallbacks包含了一些例如输入设备的配置变更,连接InputDispatcher与应用程序Socket通道,等等的接口。第二:为WMS 访问InputDispatcher提供函数实现。比如InputDispatcher中当前焦点窗口,就是WMS 通过InputMonitor来获取的。

通知应用程序窗口

InputDispatcher与应用程序的通信不是通过Binder来实现,而是通过管道channel来实现,就是Unix Domain Socket 实现,并且是一个双向的通道。因为重点是所有的应用程序都会在WMS里有注册,所以也只有WMS 能正确派发事件。

应用程序处理

在经过InputDispatcher传入事件后,就由用户的应用程序来处理最后的事件了。

首先,我们来看下事件的分发流程。

事件分发但一直没处理

分发流程

事件分发并且处理了

分发流程1

事件分发并且被layout消费

分发流程2

通过这三个图我们就可以很清楚的看到事件的分发流程顺序,并且在处理与不处理的反馈上清晰明了。

重点说下,在事件被分发到View层的时候如果同时监听了OnTouchListenerOnClickListener,那么很遗憾,如果在OnTouchListener消费了这个事件,那么clicklistener是无法接收这个事件的,原因就是因为在View派发消息时:

1
2
3
4
5
6
7
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}

mOnTouchListener.onTouch(this, event)这个函数优于onTouchEvent(event)前处理。如果想OnClickListener也可以处理也很简单,onTouch(…)返回false就行了。