输入设备通过硬件驱动向设备节点文件 /dev/input 中写入原始事件Key
Linux在EventHub中通过epoll和inotify机制,监听设备节点文件的输入事件
之后在InputReaderThread线程中通过InputReader读取输入事件,并将其交由InputDispatcherThread线程的InputDispatcher
在InputDispatcher中,InputDispatcher从InputManagerService获得当前前台窗口的Socket的服务端的文件描述符
在ActivityThread中,Activity执行完onResume之后将当前Activity的PhoneWindow的DecorView信息添加到WindowManagerService中
在添加过程中持有DecorView的ViewRootimpl的setView方法中创建与InputDispatcher通信的Socket的客户端的文件描述符
并通过WindowManagerService添加到InputManagerService中去
在ViewRootImpl中创建的Socket通信的客户端引用被内部类WindowInputEventReceiver所持有,当Native层捕获事件后
InputDispatcherThread的InputDispatcher通过Socket发送至UI线程的android_view_InputEventReceiver.cpp然后通过JNI传递至WindowInputEventReceiver的父类InputEventReceiver中,之后就开始了Java层的分发,
在Android系统中输入事件的用户消息主要分为三类,按键消息(KeyEvent),触摸消息(MoutionEvent),轨迹球消息(Trackball),其中轨迹球消息逐渐被废弃,不做讨论.
按键消息(KeyEvent):
在ViewPostImeInputStage#onProcess 根据消息类型instanceof KeyEvent 来判断当前是否是按键消息
在processKeyEvent中 如果是则传递到processKeyEvent,
在ViewRootImpl里,传递给与当前ViewRootImpl关联的DecorView的dispatchKeyEvent
在DecorView里通过在前注册在PhoneWindow的CallBack传递给Activity的dispatchKeyEvent
在Activity类dispatchKeyEvent里面再次将事件传递给当前Window的DecorView的superDispatchKeyEvent
(Activity的dispatchKeyEvent中可以拦截当前Activity的输入事件)
在DecorView中将事件传递给其父类ViewGroup的dispatchKeyEvent
此时便开始了View树的事件派发
在View的dispatchKeyEvent 中先执行onKey监听如果事件没被消费则走系统默认的消息处理流程,即KeyEvent的dispatch方法
其中dispatch的第一个参数KeyEvent.CallBack,这个回调在View和Activity中均有实现,当前传入的是View
在dispatch中先执行View的onKeyDown,然后执行onKeyUp,并在onKeyUp中区分长按事件还是点击事件
如果遍历完View树事件依旧没有被消费,又回到了Activity的dispatchKeyEvent中,开始了Activity的消息处理
直接调用KeyEvent的dispatch这时传入的参数是当前Activity
在dispatch中先执行Activity的onKeyDown,然后执行onKeyUp,并在onKeyUp中区分长按事件还是点击事件
当按键消息在View树中内部以及Activity内部没有被处理时,再次回到之前调用PhoneWindow的CallBack的地方
调用PhoneWindow的onKeyDown以及onKeyUP,在这里面处理一些系统按键如音量键,Menu键
触摸消息(MoutionEvent):
在ViewPostImeInputStage的onProcess中根据事件来源区分然后将事件传递
给PhoneWindow中的DecorView的dispatchPointerEvent方法(DecorView没有重写该方法,实际在View里)
接着调用DecorView里面的dispatchTouchEvent在之后就是将事件通过Window.Callback传递给Activity的dispatchTouchEvent
在Activity的dispatchTouchEvent里面再次事件传递回DecorView,接着就开始了正式的View树的事件分发从DecorView的
父类ViewGroup开始dispatchTouchEvent
在ViewGroup的dispatchTouchEvent中先执行onInterceptTouchEvent确定是否需要继续向下传递
然后开始计算触摸事件消息位置是否包含了该child如果是,当前View是否还是ViewGroup如果是
则继续执行ViewGroup的dispatchTouchEvent以及onInterceptTouchEvent,
在找到目标View之后,调用目标View的dispatchTouchEvent,然后是目标View的onTouchEvent
在onTouchEvent中执行View的长按判断以及点击事件
如果没有retuan true那么在ViewGroup的dispatchTouchEvent开始执行ViewGroup的onTouchEvent
ViewGroup里面的onTouchEvent其实调用的就是View里面的
当所有的ViewGroup的onTouchEvent中都没消费事件
回到Activity的dispatchTouchEvent并调用Activity的onTouchEvent
触摸消息分发与按键消息派发区别
- 1.触摸消息在处理时,需要根据触摸坐标计算该消息位置是否包含了当前View而决定是否需要分发下去而按键消息则是根据当前View或则子View是否有焦点而决定是否需要分发下去
- 2.触摸消息没有类似“系统按键”,“系统组合键”等预设消息需要处理