本文主要分析触摸事件和按键事件在不同阶段被拦截的流程,总结在不同阶段不同方法中返回值的含义。
按键的拦截
(1)interceptKeyBeforeQueueing
interceptKeyBeforeQueueing方法的意义就是在事件入队列前拦截按键事件,也就是如果这个阶段被拦截,事件将不会再被分发。
在分发keyevent事件前,会先将event上报给PhoneWindownManager处理,如果PhoneWindowManager消费掉,则不会继续分发给应用,流程如下:
调用栈如下:
InputDispatcher::injectInputEvent // 触发调用1
InputDispatcher::notifyKey // 触发调用2
com_android_server_input_InputManagerService::NativeInputManager::interceptKeyBeforeQueueing
InputManagerService::interceptKeyBeforeQueueing
IMS::InputManagerCallback::interceptKeyBeforeQueueing
WMS::PhoneWindowManager::interceptKeyBeforeQueueing
- InputDispatcher.notifyKey会通过JNI的方式向PhoneWindowManager通报事件,先交由PhoneWindowManager来处理一些系统的按键,如果PhoneWindowManager已经处理消费且认为按键事件不应再分发给应用程序,则会返回0,否则返回1(ACTION_PASS_TO_USER);
我们在日常开发中,有时候希望事件在系统中消费掉,不传递给应用进行处理,那么可以在这个阶段添加我们的逻辑,只需要将PhoneWindowManager.interceptKeyBeforeQueueing返回0即可。
(2)interceptKeyBeforeDispatching
interceptKeyBeforeDispatching是在事件分发前进行拦截,同样也是交给PhoneWindomManager来进行处理,流程如下:
调用栈如下:
InputDispatcher::dispatchOnce
InputDispatcher::dispatchOnceInnerLocked
//InputDispatcher::mPolicy // mPolicy:: com_android_server_input_InputManagerService.cpp
InputDispatcher::dispatchKeyLocked
InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible
com_android_server_input_InputManagerService::NativeInputManager::interceptKeyBeforeDispatching
// jni InputManagerService::nativeInit中初始化
InputManagerService::interceptKeyBeforeDispatching
IMS::InputManagerCallback::interceptKeyBeforeDispatching
WMS::PhoneWindowManager::interceptKeyBeforeDispatching
- InputDisptcher调用dispatchKeyLocked来处理InputReader发送过来的事件,此时的KeyEntry.interceptKeyResult为INTERCEPT_KEY_RESULT_UNKNOWN,如果此事件在上一阶段被设置为POLICY_FLAG_PASS_TO_USER,那么就会生成一个command,去交给PhoneWindowManager去处理;
- PhoneWindowManager调用interceptKeyBeforeDispatching来处理一些特殊的key事件,如power、home等,然后返回事件被处理的时机(延时),如果事件应被拦截,则返回-1,立即分发则返回0,延迟分发则返回延时的时间;
- InputDispatcher在处理command的时候根据PhoneWindowManager处理的返回值,将KeyEntry.interceptKeyResult置为不同的flag;
- 由于InputDispatcher loop里每次只处理一个事件,而每次循环都会优先处理堆积的command,而且mPendingEvent只有当事件被处理完才会置null,因此在下一次loop的时候,处理完command就立即继续处理这个key事件,根据interceptKeyResult来选择是否拦截;
前面两个阶段都是框架层Framework将事件拦截,下面的dispatchKeyEvent主要讲的是应用所能修改的,按照调用的逻辑层次来分析。
- 调用View.dispatchKeyEvent(mView就是DecorView),当Window没有被destory,走Activity.dispatchKeyEvent -> PhoneWindow.superDispatchKeyEvent -> DecorView.superDispatchKeyEvent -> ViewGroup.dispatchKeyEvent -> View.dispatchKeyEvent。此调用链中,Activity.dispatchKeyEvent、View.dispatchKeyEvent都是应用开发常常override的方法;
- 在Activity.dispatchKeyEvent方法(默认实现)中,当PhoneWindow.superDispatchKeyEvent返回了false值,则走KeyEvent.dispatch方法,然后回调Activity的onKeyDown/onKeyUp等方法;
- 如果Activity.dispatchKeyEvent方法返回了false,则调用到PhoneWindow的onKeyDown/onKeyUp方法;
(3)KeyEvent的拦截总结
触摸事件的拦截
(1)dispatchTouchEvent
在ViewRootImpl阶段,拦截的过程如下:
- InputDispatch将事件分发给对应的ViewRootImpl中,继而将事件分发给DecorView,然后调用到Activity.dispatchTouchEvent -> ViewGroup.dispatchTouchEvent -> View.dispatchTouchEvent。当有一个流程返回true,则将事件拦截;
- 当Activity.dispatchTouchEvent返回false没有拦截事件,则会调用Activity.onTouchEvent来处理;
(2)MotionEvent的拦截总结
想了解通俗易懂的应用事件分发机制,可参考如下文章:Android事件拦截(二)——通俗易懂事件分发机制。