这是 ZY 的第 24 篇原创 上篇文章我们站在面试官的角度学了 View 的绘制流程,这篇文章再来说说
安卓的事件分发。
01
题目层次 面试中提到安卓的事件分发,我们一般都能说到从 Activity -> Window -> DecorView -> ViewGroup -> View 的 dispatchTouchEvent 流程,这个是最基本的需要掌握的,由此能深入引出一些什么知识点呢?
其中主要有几个阶段:
我们看一下 native 侧的 InputManagerService 初始化代码。
- 事件是如何从屏幕点击最终到达 Activity 的?
- CANCEL 事件什么时候会触发?
- 如何解决滑动冲突?
2.1 安卓事件的分发
安卓的事件分发大概会经历 Activity -> PhoneWindow -> DecorView -> ViewGroup -> View 的 dispatchTouchEvent。 其中 dispatchTouchEvent 用下面的一段伪代码就可以说明了,过程就不具体分析了,大家应该也都比较清晰。
// 伪代码
public boolean dispatchTouchEvent() {
boolean res = false;
// 是否不允许拦截事件
// 如果设置了 FLAG_DISALLOW_INTERCEPT,不会拦截事件,所以在 child 里可以通过 requestDisallowInterceptTouchEvent 控制父 View 是否来拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept && onInterceptTouchEvent()) { // View 不调用这里,直接执行下面的 touchlistener 判断
if (touchlistener && touchlistener.onTouch()) {
return true;
}
res = onTouchEvent(); // 里面会处理点击事件 -> performClick() -> clicklistener.onClick()
} else if (DOWN) { // 如果是 DOWN 事件,则遍历子 View 进行事件分发
// 循环子 View 处理事件
for (childs) {
res = child.dispatchTouchEvent();
}
} else {
// 事件分发给 target 去处理,这里的 target 就是上一步处理 DOWN 事件的 View
target.child.dispatchTouchEvent();
}
return res;
}
2.2 事件是如何到达 Activity 的
既然上面的事件分发是从 Activity 开始的,那事件是怎么到达 Activity 的呢? 总体流程大概是这样的:用户点击设备, linux 内核接受中断, 中断加工成输入事件数据写入对应的设备节点中, InputReader 会监控 /dev/input/ 下的所有设备节点, 当某个节点有数据可以读时,通过 EventHub 将原始事件取出来并翻译加工成输入事件,交给 InputDispatcher,InputDispatcher 根据 WMS 提供的窗口信息把事件交给合适的窗口,窗口 ViewRootImpl 派发事件。 大体流程图如下:![205eb55ff14b2c02f12b94d68bc26bde.png](https://img-blog.csdnimg.cn/img_convert/205eb55ff14b2c02f12b94d68bc26bde.png)
- 硬件中断
- InputManagerService 做的事情
- InputReaderThread 做的事情
- InputDispatcherThread 做的事情
- WindowInputEventReceiver 做的事情
2.2.1 硬件中断
硬件中断这里就简单介绍一些,操作系统对硬件事件的接收是通过中断来进行的。
内核启动的时候会在中断描述符表中对中断类型以及对应的处理方法的地址进行注册。
当有中断的时候,就会调用对应的处理方法,把对应的事件写入到设备节点里。
2.2.2 InputManagerService 做的事情
InputManagerService 是用来处理 Input 事件的,Java 侧的 InputManagerService 就是 C++ 代码的一个封装,以及提供了一些 callback 用来传递事件到 Java 层。我们看一下 native 侧的 InputManagerService 初始化代码。
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp& looper) :
<