Android 是基于“事件驱动”模型的。所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),系统执行什么操做(即调用什么函数)。固然事件不只限于用户的操做,事件驱动的核心天然是事件。java
从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。事件收集器专门负责收集全部事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操做系统、应用程序自己等)。android
从广义上来讲,事件的发生源能够分为软件
和硬件
两类,这里侧重对硬件
部分的讨论。c++
事件是由真实物理硬件产生的消息,代表设备使用者的某种意愿。markdown
若是从硬件设备角度为事件分类,最重要的有如下几种分类app
按键事件 (KeyEvent)socket
由物理按键产生的事件。async
触摸事件 (TouchEvent)ide
在触摸屏上的点击、滑动等事件,是 Android 系统中使用最普遍的事件。函数
鼠标事件 (MouseEvent)oop
鼠标操做引发的事件,如单击、双击、移动等。
Android 针对上面提到的事件,提取出了一个统一的抽象接口,这就是 InputEvent 。
InputEvent 下面有两个类:KeyEvent 和 MotionEvent。KeyEvent 用于表达按键事件,MotionEvent 则是将因此能产生 MoveEvent 的事件源进行统一管理。
事件源产生后,Linux 内核收集到原始信息,Android 系统经过 /dev/input 下的节点来访问当前的发生的事件并对 原始数据 进行提炼,而后交由 WMS 去分配,WMS 做为 InputEvent 的派发者,将事件派发到指定的 window 去处理,当事件到达应用程序端的时候,就已经变成相对 可理解、好处理 的了。
接下来咱们从 InputManagerService 入手,看看 InputEvent 事件是如何一步步变得好处理的。
InputManagerService 是由 SystemServer 统一启动
/*framework/base/services/java/com/android/server/SystemServer.java */ private void startOtherServices() { ... inputManager = new InputManagerService(context); wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); inputManager.start(); 复制代码
从上面能够看出,InputManagerService 的实例直接做为 WindowManagerService 的参数传入,另外, IMS 也将本身注入到了 ServiceManager 中。最后调用 inputManager.start()
启动 IMS。
/*framework/base/services/java/com/android/server/input/InputManagerService.java */ public void start() { nativeStart(mPtr); // mPtr 是 IMS 在构造过程当中调用 nativeInit 所建立的 NativeInputManagerService 对象 Watchdog.getInstance().addMonitor(this); // 将 IMS 加入监控体系 /* 接下来是一些监听事件处理,这里的实现包含大量的 native 调用 */ } 复制代码
看来主要的事情都是 NativeInputManagerService 来处理的,接下来看看 NativeInputManagerService 的实现
/*framework/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/ static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == nullptr) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im); } // nativeInit 初始化了 NativeInputManager 对象 static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_t result = im->getInputManager()->start(); // getInputManager 返回的是 InputManager 对象 if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } } 复制代码
nativeStart 最终实现的是 InputManager.start()
方法, InputManager 为 IMS 建立了2 个线程,分别是 InputReaderThread
和InputDispatcherThread
,其中InputReaderThread
负责从从驱动节点读取 Event,InputDispatcherThread
专职分发。咱们重点关注 Dispatcher 的部分,对于 Reader 部分这里不作细说。
/*framework/native/services/inputflinger/InputManager.cpp*/ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); mReader = createInputReader(readerPolicy, mClassifier); // mReader 与 mDispatcher 在这里创建了关系,如此 Reader线程 就有了通道将事件告知 Dispatcher 线程去执行分发流程 initialize(); } void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); //mDispatcherThread 的核心实现主要由 InputDispatcher 来实现 } status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputReader thread due to error %d.", result); mDispatcherThread->requestExit(); return result; } return OK; } 复制代码
接下来主要关注 InputDispatcher 的分发部分,InputDispatcher 将它获知到的系统事件使用 EventEntry 结构表示,能够分为如下几类
enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; 复制代码
本文讨论的事件主要是这里的 TYPE_KEY 类型;InputDispatcher 分发事件的核心函数为InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime)
,其中针对 TYPE_KEY
类型执行的函数为done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)
/*framework/native/services/inputflinger/InputDispatcher.cpp*/ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { .... 省略了前面一些前期工做与拦截处理 std::vector<InputTarget> inputTargets; int32_t injectionResult = ***findFocusedWindowTargetsLocked***(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } setInjectionResult(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // 分发事件到目标中去 dispatchEventLocked(currentTime, entry, inputTargets); return true; } 复制代码
dispatchKeyLocked 先经过 findFocusedWindowTargetsLocked() 来寻找目标窗口,成功找到后会将其加入 inputTargets 中,而且将 injectionResult 赋值为 INPUT_EVENT_INJECTION_SUCCEEDED ;最终经过dispatchEventLocked将事件分发给目标窗口。
findFocusedWindowTargetsLocked() 能拿到目标窗口,那这里面确定少不了 WMS 的参与,由于 WMS 才是真正 window 的管家。还记得最开始,IMS 与 WMS 绑定的那些代码吗?具体的讲,WMS 内部 持有一个 InputManagerCallback 的回调 mInputManagerCallback,IMS 又经过 setWindowManagerCallbacks() 函数将 mInputManagerCallback 做为入参传入,这InputManagerCallback 定义了一系列与事件相关的接口,当 Window 变更的时候,WMS 都会经过 InputManagerCallback 来与 IMS 交流。更细点来讲,WMS 到 InputDispatcher 的过程当中使用到了 InputMonitor 这个"中介",感兴趣的能够自行去研究。
dispatchEventLocked() 这个函数负责将事件通知给应用程序去处理的, InputDispatcher与 InputTarget 通讯是经过 InputChannel 来进行的, InputChannel 是经过 socket 来进行通讯的。具体流程是 WMS 在调用 addWindow()
方法时候通会经过 InputChannel.openInputChannelPair(name)
方法打开一个双向通道,而这个openInputChannelPair (源码路径/* framework/native/libs/input/InputTransport.cpp */) 在 native 层的实现就是经过 socket 实现的。
经过上面的分析,咱们对 native 层的事件与 WMS 的传递大体有了了解,而咱们更为关心的则是应用层的事件传递。咱们知道,Activity 调用addView()
将 Window 添加到 WMS 的过程当中会使用到 ViewRootImpl 这个中介,咱们就从 ViewRootImpl 开始分析这一流程。
WMS 与 View 沟通的桥梁是经过 ViewRootImpl 来实现的。事件能回传给 View 的前提是 View 所在的 Window 添加到了 WMS 中,而 View 添加到 WMS 的过程必然会走到 ViewRootImpl.setView()
方法,那么就从这个方法开始跟踪。
/*framework/base/core/java/android/view/ViewRootImpl.java*/ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ... if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets); catch (RemoteException e) { mInputChannel = null; }finally { ... } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } // WindowInputEventReceiver 继承了 InputEventReceiver mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix); InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, "aq:native-pre-ime:" + counterSuffix); mFirstInputStage = nativePreImeStage; mFirstPostImeInputStage = earlyPostImeStage; 复制代码
若是 Window 支持事件响应,会 new 一个 InputChannel 对象,而后经过 WindowSession.addToDisplay()
将其做为参数传入 WMS ,以后经过 InputEventReceiver 来监听 InputChannel 的回传数据。结合前面 dispatchEventLocked() 中提到的 InputChannel 充当的角色,不难理解,InputEventReceiver 最终能收到 native 层的回传消息。
以后以责任链的形式封装了 InputStage,默认状况下责任链的起点是 NativePreImeInputStage,终点为 SyntheticInputStage,(固然这不是绝对的,mSyntheticInputStage、mFirstInputStage、mFirstPostImeInputStage 这三个参数就是来肯定责任链真正的起点的)针对上层 View 的分发逻辑在 ViewPostImeInputStage 中。根据这点,不难猜想到 WindowInputEventReceiver 在处理分发的过程当中会将事件分发给这个 InputStage 的责任链去处理。实际流程也是如此。归纳来讲就是,WindowInputEventReceiver 收到事件后,WindowInputEventReceiver.onInputEvent()
开始执行,它的职责是将已经消费的事件通知底层,将未消费的事件经过 enqueueInputEvent()
加入队,enqueueInputEvent()
中的doProcessInputEvents()
会不断获取当前须要处理的 InputEvent 而且将事件分发给 InputStage 去处理。当 Event 被 InputStage 处理完毕后,最终将处理结果经过InputEventReceiver.finishInputEvent()
通知 native 层。
/*framework/base/core/java/android/view/ViewRootImpl.java*/ private void deliverInputEvent(QueuedInputEvent q) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", q.mEvent.getSequenceNumber()); if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); } // 这里肯定分发责任链的起点 InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } // 对 KeyEvent 的额外处理 if (q.mEvent instanceof KeyEvent) { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); // 将事件分发给指定 InputStage 去处理 } else { finishInputEvent(q); } } /** * Delivers an event to be processed. */ public final void deliver(QueuedInputEvent q) { if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (shouldDropInputEvent(q)) { finish(q, false); } else { apply(q, onProcess(q)); // 这里执行 InputStage 的 onProcess 方法 } } 复制代码
接下来重点关注ViewPostImeInputStage.onProcess()
方法,对其余 InputStage 感兴趣的同窗能够经过翻阅源码自行阅读,流程都差很少的。从 processGenericMotionEvent 中能够看出,事件又被分发给了 View Tree 的根元素 mView ( 大部分状况下是Activity )去处理,如此一来 变开启了 View 的事件分发流程。
/*framework/base/core/java/android/view/ViewRootImpl$ViewPostImeInputStage.java*/ protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); //若是是 KeyEvent 走 KeyEvent 的处理流程 } else { final int source = q.mEvent.getSource(); // 获取事件源类型 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); // MotionEvent 类型的事件会执行到这来 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q); // 轨迹球事件类型 } else { return processGenericMotionEvent(q); // Motion 事件 } } } private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; // 将事件传递给 view 的 dispatchPointerEvent 处理 boolean handled = mView.dispatchPointerEvent(event); return handled ? FINISH_HANDLED : FORWARD; } 复制代码
View 中处理的事件主要为 TouchEvent ,它能够细分为如下几个重要类型:
ACTION_DOWN
当手势按下就会触发,是后续一切事件的起点,通常会在这里作一些状态初始化工做。
ACTION_MOVE
当手势按下并拖动,就会产生 ACTION_MOVE 事件。
ACTION_UP
ACTION_UP 是手势操做的结束点。这里经过与 ACTION_DOWN 时候设置的参数进行比较就能够判断是否为长按事件等。
ACTION_CANCEL
ACTION_CANCEL 事件不禁用户主动产生,而是系统经过判断后得出的结果。能够简单看作是手势结束的标识,处理相似于 ACTION_UP,并作好清理工做。
/*framework/base/core/java/android/view/View.java */ public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } } public boolean dispatchTouchEvent(MotionEvent event) { boolean result = false; if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } return result; } 复制代码
View 中分发 TouchEvent 仍是比较简单的,主要考虑到了两个因素:
onTouch
View 对象能够经过 setOnTouchListener 来设置一个 Event 的监听者,对应上面的 li.mOnTouchListener ,这样当事件来临时,View 会主动调用这个监听者的 onTouch 方法去消费事件。
onTouchEvent
用户没有指定 TouchListener ,或者 flag 指定为 disable, 亦或是用户指定了 TouchListener 可是 TouchListener.onTouch() 返回值为 false 的状况下,系统才会将 event 传递给 onTouchEvent 。
ViewGroup 的 TouchEvent 投递流程相对复杂一些,由于涉及到对子对象的处理,多了一个拦截的概念。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // ACTION_DOWN 事件来临,先清除掉以前的全部状态 cancelAndClearTouchTargets(ev); resetTouchState(); } // 检查拦截状况 final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // 若是disallowIntercept 为 false,调用 onInterceptTouchEvent 方法 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; //不拦截 } } else { intercepted = true; //拦截 } // 检查 CANCEL 的状况 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { //不拦截的状况 if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); for (int i = childrenCount - 1; i >= 0; i--) { // 省略了循环查找一个能处理此事件的 child 的过程 if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. ... break; } .... return handled } 复制代码
ViewGroup 的 dispatchTouchEvent 首先会检查是否被拦截,用intercepte来表示,若是 intercepted 为 false,代表 ViewGroup 不但愿拦截这一消息,于是它的子对象才有机会来处理它。若是 intercepted 为 true,说明 ViewGroup 须要拦截这个事件本身来处理,以后经过 dispatchTransformedTouchEvent()
的super.dispatchTouchEvent()
触发 onTouchEvent()
回调。
intercepted 要为 true 须要知足两个条件:
disallowIntercept 为 false
能够经过调用 ViewParent 接口的 requestDisallowInterceptTouchEvent(true) 使 disallowIntercept 为false,Android 中仅 ViewGroup 和ViewRootImpl 继承了 ViewParent 接口,而 ViewRootImpl 中的 requestDisallowInterceptTouchEvent 是一个空实现。
也就是说,只有 ViewGroup.requestDisallowInterceptTouchEvent(true) 有效。
onInterceptTouchEvent() 返回值为 true
onInterceptTouchEvent() 能够体现出每一个 ViewGroup “差别化”的地方,这个方法是 Android 鼓励你们在继承 ViewGroup 时候重载的方法。
canReceivePointerEvents()
用于判断当前 child 是否能接收 PointerEvents,dispatchTransformedTouchEvent()
则计算(x,y)这个点有没有落在此 child 区域; 若是 child 不符合要求,直接跳过,能够节省时间。若是找到了符合要求的 child,就经过 dispatchTransformedTouchEvent()
来调用child.dispatchTouhEvent()
,这个 child 有多是 View,也有多是 ViewGroup。
Activity 的 dispatchTouchEvent 首先会将事件交给它的 window 对象去处理,Activity 的 getWindow() 获得的 Window 对象是 PhoneWindow,PhoneWindow 的 superDispatchTouchEvent() 方法又将事件分发给 mDecor 去处理,这个 mDecor 是一个 FrameLayout,是在 Activity 调用 setContentView() 时生成的。具体的流程这里再也不展开,对这块有疑问的同窗能够自行跟踪 setContentView() 的调用流程。FrameLayout 又是继承 ViewGroup 的,如此便和上面 View 的 dispatchTouchEvent 创建了关联。
/*framework/base/core/java/android/app/Activity.java*/ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); } 复制代码
/*framework/base/core/java/com/android/internal/policy/PhoneWindow.java*/ @Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); } 复制代码