[TOC]
文章参考:https://mp.weixin.qq.com/s/YyJu3b_-InWjgyVXz48B4g
概述 我们将整个Touch事件可以分解为以下几个部分:
Touch事件如何从屏幕到我们的App
Touch事件到达App后怎么传递到对应页面
Touch事件到达对应页面后内部怎样分发
其中与上层软件开发息息相关的就是第3条,也是我们最关注的,它也可以拆解为以下几个问题
ViewGroup是否拦截事件,拦截与不拦截后分别怎么处理?
子View是否拦截事件,拦截与不拦截后分别怎么处理?
ViewGroup与子View都不拦截,最终事件如何处理?
如何处理事件冲突?
从屏幕到APP 硬件与内核部分 当我们触摸屏幕或者按键操作时,首先触发的是硬件驱动
驱动收到事件后,将相应事件写入到输入设备节点,这便产生了最原生态的内核事件 当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下
这样做的目的是将输入事件封装为通用的Event,供后续处理
SystemServer部分 我们知道,当系统启动时,在SystemServer进程会启动一系列系统服务,如AMS,WMS等。
其中还有一个就是我们管理事件输入的InputManagerService。这个服务就是用来负责与硬件通信,接受屏幕输入事件。
在其内部,会启动一个读线程,也就是InputReader,它会从系统也就是/dev/input/目录拿到任务,并且分发给InputDispatcher线程,然后进行统一的事件分发调度。
跨进程通信传递给App 现在系统进程已经拿到输入事件了,但还需要传递给App进程,这就涉及到跨进程通信的部分
我们的App中的Window与InputManagerService之间的通信实际上使用的InputChannel InputChannel是一个pipe,底层实际是通过socket进行通信。
我们知道在Activity启动时会调用ViewRootImpl.setView()
在ViewRootImpl.setView()过程中,也会同时注册InputChannel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public void setView (View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this ) { mInputChannel = new InputChannel (); try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true ; collectViewAttributes(); adjustLayoutParamsForCompatibility(mWindowAttributes); res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mDisplayCutout, inputChannel, mTempInsets, mTempControls); setFrame(mTmpFrame); } catch (RemoteException e) { mAdded = false ; mView = null ; mAttachInfo.mRootView = null ; inputChannel = null ; mFallbackEventHandler.setView(null ); unscheduleTraversals(); setAccessibilityFocus(null , null ); throw new RuntimeException ("Adding window failed" , e); } finally { if (restore) { attrs.restore(); } } } } }
这里涉及到了WindowManagerService和Binder跨进程通信,读者不需要纠结于详细的细节
只需了解最终在SystemServer进程中,WindowManagerService根据当前的Window创建了SocketPair用于跨进程通信,同时并对App进程中传过来的InputChannel进行了注册
这之后,ViewRootImpl里的InputChannel就指向了正确的InputChannel, 作为Client端,其fd与SystemServer进程中Server端的fd组成SocketPair, 它们就可以双向通信了。
然后我们App进程的主线程就会监听socket客户端,当收到消息(输入事件)后,回调NativeInputEventReceiver.handleEvent()方法,最终会走到InputEventReceiver.dispachInputEvent方法。
经过以上操作App终于拿到输入事件了,接下来就是传递到对应页面。
从APP到页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver (InputChannel inputChannel, Looper looper) { super (inputChannel, looper); } @Override public void onInputEvent (InputEvent event) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility" ); List<InputEvent> processedEvents; try { processedEvents = mInputCompatProcessor.processInputEventForCompatibility(event); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (processedEvents != null ) { if (processedEvents.isEmpty()) { finishInputEvent(event, true ); } else { for (int i = 0 ; i < processedEvents.size(); i++) { enqueueInputEvent( processedEvents.get(i), this , QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true ); } } } else { enqueueInputEvent(event, this , 0 , true ); } } }
可以看到事件还是回到了ViewRootImpl中,可见ViewRootImpl不仅负责界面的绘制,同时负责事件的传递.
进入enqueueInputEvent的时间队列处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @UnsupportedAppUsage void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys // are processed in the order they were received and we cannot trust that // the time stamp of injected events are monotonic. QueuedInputEvent last = mPendingInputEventTail; if (last == null) { mPendingInputEventHead = q; mPendingInputEventTail = q; } else { last.mNext = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1; Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }
接下来走到doProcessInputEvents中,其中涉及到事件分发中的第一次责任链分发
第一次责任链分发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 private void scheduleProcessInputEvents () { if (!mProcessInputEventsScheduled) { mProcessInputEventsScheduled = true ; Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); msg.setAsynchronous(true ); mHandler.sendMessage(msg); } } void doProcessInputEvents () { while (mPendingInputEventHead != null ) { QueuedInputEvent q = mPendingInputEventHead; mPendingInputEventHead = q.mNext; if (mPendingInputEventHead == null ) { mPendingInputEventTail = null ; } q.mNext = null ; mPendingInputEventCount -= 1 ; Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); long eventTime = q.mEvent.getEventTimeNano(); long oldestEventTime = eventTime; if (q.mEvent instanceof MotionEvent) { MotionEvent me = (MotionEvent)q.mEvent; if (me.getHistorySize() > 0 ) { oldestEventTime = me.getHistoricalEventTimeNano(0 ); } } mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime); deliverInputEvent(q); } if (mProcessInputEventsScheduled) { mProcessInputEventsScheduled = false ; mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); } } private void deliverInputEvent (QueuedInputEvent q) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent" , q.mEvent.getId()); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x" + Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano=" + q.mEvent.getEventTimeNano() + " id=0x" + Integer.toHexString(q.mEvent.getId())); } try { if (mInputEventConsistencyVerifier != null ) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "verifyEventConsistency" ); try { mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0 ); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager" ); try { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } if (stage != null ) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
那么问题来了,InputStage的责任链是什么时候组件的呢?
组装责任链 我们得回到ViewRootImpl.setView方法中