回顾
在 上一篇文章 中,我们探索了从内核触摸事件传递到 InputDispatcher
线程,再与应用线程之间建立 InputChannel
的过程,并且我们已经看到一个最原始的触摸事件被封装成一个 msg
并通过 InputChannel
建立的 socket
通过 sendMessage()
方法跨线程通信发送给了应用的UI线程。
这篇文章将会看到应用UI线程的消息队列是如何读取传递过来的触摸事件并进行处理、分发的。
本篇文章主要参考了 Gityuan的文章
消息循环
Android
的消息机制的具体内容在这里不详细叙述:
每个线程都可以拥有一个自己的消息队列与一个 Looper
,在 Looper
初始化的过程中,会启动一个循环来不断读取、处理队列中的消息, Android
是一个事件驱动的模型,只有源源不断的事件产生与处理才能推动应用的进行。
同时应该注意的是在 Java
与 Native
中各有一套消息处理的流程可以进行消息的处理。
Looper
当应用初始化时,会调用 Looper.prepare()
:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
会在 ThreadLocal
区域新建一个 Looper
对象:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
同时初始化了一个 MessageQueue
,保存了当前的线程:
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
nativeInit()
方法初始化了 native
的消息队列:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jlong>(nativeMessageQueue); }
新建了一个 NativeMessageQueue
:
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }
这里进行的初始化过程与 java
层的比较类似,都是新建了一个 Looper
对象存放入了 ThreadLocal
区域中。
当初始化过程完成之后,系统调用 Looper.loop()
开始消息循环:
public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... msg.recycleUnchecked(); } }
省略了大量代码,我们看到在这个无限循环中,首先就调用了 MessageQueue
的 next()
方法来获取下一条消息,注意这是一个阻塞调用,在下一条消息还没到时间或者没有下一条消息时候会被阻塞。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); ... } }
这里我们不关心 java
层后续对事件的处理,而是关心 java
层是如何调用 native
层的方法来对 native
消息队列中的事件进行处理的,因为我们的触摸事件是在 native
层进行处理再到 java
层进行分发的。
在 next()
方法中我们就调用了 nativePollOnce()
方法先去处理 native
中的事件:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); }
调用了 nativeMessageQueue
的 pollOnce()
方法:
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; mLooper->pollOnce(timeoutMillis); mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); mExceptionObj = NULL; } }
调用了 native
Looper
的 pollOnce()
方法:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { ... result = pollInner(timeoutMillis); } }
忽略特殊处理的过程,最终调用了 pollInner()
方法:( PollInner()
的代码比较长,省略了大部分,标记了后面讨论的三个部分)
int Looper::pollInner(int timeoutMillis) { ...// 省略初始化过程 // Poll. int result = POLL_WAKE; mResponses.clear(); mResponseIndex = 0; // We are about to idle. mPolling = true; /*-------1-------*/ struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); ... /*---------------*/ /*-------2-------*/ for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; if (epollEvents & EPOLLIN) events |= EVENT_INPUT; if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } } } Done: ; ...// 省略native message处理过程 // Release lock. mLock.unlock(); /*---------------*/ /*-------3-------*/ // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); if (response.request.ident == POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif // Invoke the callback. Note that the file descriptor may be closed by // the callback (and potentially even reused) before the function returns so // we need to be a little careful when removing the file descriptor afterwards. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll. response.request.callback.clear(); result = POLL_CALLBACK; } } /*---------------*/ return result; }
第一部分中,调用了 epoll_wait()
函数等待消息,当接收到消息或者发生超时的时候调用返回。
第二部分对返回的 events
进行遍历,如果对应的 fd
为唤醒专用的 mWakeEventFd
,执行 awoken()
函数清空管道,这个事件的作用只是为了唤醒 Looper
对新消息进行处理。
如果不是 mWakeEventFd
,说明为我们之前通过 addFd()
函数添加的 fd
,我们需要对这个 event
进行处理,处理函数为 pushResponse()
:
ssize_t requestIndex = mRequests.indexOfKey(fd); pushResponse(events, mRequests.valueAt(requestIndex));
我们还记得在前面 addFd()
的过程中已经将 fd
作为索引,向 mRequest
中保存了 request
信息,信息中包含了 callback
也就是 NativeInputEventReceiver
对象。
void Looper::pushResponse(int events, const Request& request) { Response response; response.events = events; response.request = request; mResponses.push(response); }
这里将 request
对象包装成了一个 response
,然后存入了 mResponses
中等待后面的处理。
第三部分中就是对于 response
的处理过程,主要就是这个调用:
int callbackResult = response.request.callback->handleEvent(fd, events, data);
调用了 callback
的 handleEvent()
,我们现在知道 callback
是前面保存的 NativeInputEventReceiver
对象。
现在,当原始事件通过建立好的 InputChannel
的 sendMessage()
函数发送触摸事件时:
status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; do { nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); ... return OK; }
会直接调用 send()
函数向 fd
中写入数据,同时在另一边的 epoll_wait()
调用就会因 fd
数据的到来而唤醒,并通过 fd
找到注册好的 request
,进而调用 request
中的 NativeInputEventReceiver
的 handleEvent()
方法,参数就是我们接收到的事件信息与数据。
handleEvent
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { ... if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } ... return 1; }
调用了 consumeEvents()
函数来处理事件,函数较长,我们拆开来看:
函数进行初始化过程之后执行了一个无限循环,循环体中的内容如下:
InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent);
首先就调用了 mInputConsumer
对象的 consume
方法接收并将原始的事件转换为分发过程中标准的 MotionEvent
:
status_t result = mChannel->receiveMessage(&mMsg);
这里就直接调用了 InputChannel
的 receiveMessage()
函数来接收另一端发送来的消息。
switch (mMsg.header.type) { case InputMessage::TYPE_KEY: { ... } case AINPUT_EVENT_TYPE_MOTION: { ... MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; updateTouchState(&mMsg); initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().string(), *outSeq); #endif break; } }
这里对事件的类型进行了一个判断,当类型为 MOTION
即触摸事件时,新建了一个 MotionEvent
,然后用 mMsg
去进行初始化:
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } event->initialize( msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, msg->body.motion.xOffset, msg->body.motion.yOffset, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); }
然后在第14行把它存入了 outEvent
(也就是 consume()
函数中传入的 inputEvent
)中,现在函数返回到 NativeInputEventReceiver::consumeEvents()
继续处理:
switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: ... case AINPUT_EVENT_TYPE_MOTION: { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); } MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent); if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) { *outConsumedBatch = true; } inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent); break; } ... } if (inputEventObj) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); } env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); ... }
下面就对 inputEvent
(即为 MotionEvent
)的类型作了一个判断,对 inputEventObj
(用于调用 java
层方法)进行赋值。随后就通过 JNI
的 CallVoidMethod()
方法来调用 java
层的 dispatchInputEvent()
方法。这里调用的是 java
层 InputEventReceiver
的 dispatchInputEvent()
方法:
开始分发
dispatchInputEvent
private void dispatchInputEvent(int seq, InputEvent event) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); }
InputEventReceiver
是一个抽象类,具体实现类是 ViewRootImpl
的内部类 WindowInputEventReceiver
,它覆盖了 onInputEvent()
方法:
@Override public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); }
调用了 ViewRootImpl
的 enqueueInputEvent()
方法:
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { adjustInputEventForCompatibility(event); 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(); } }
将接收到的事件加入了 mPendingInutEvent
链表的头部,注释里给出了这么做的原因:当发生事件插入的时候我们不能依赖事件的时间戳是准确的,因此必须让最新收到的事件先进行处理。
最终调用 doProcessInputEvents()
进行事件处理:
void doProcessInputEvents() { // Deliver all pending input events in the queue. 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); } ... }
在从链表中取出事件之后,对事件的时间戳进行了更新。然后调用 deliverInputEvent()
方法:
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; } if (stage != null) { stage.deliver(q); } else { finishInputEvent(q); } }
这段代码第一眼看上去比较难懂, Stage
让我们联想到了CPU流水线处理过程中的 Stage
,这里就是进入了一个流水线过程来处理事件:
流水线事件处理
首先看到我们可以根据事件类型的需要从 mSyntheticInputStage
EarlyPostImeInputStage
NativePreImeInputStage
三个入口进入流水线,而流水线的每一步都对事件进行了不同的处理,并可以通过 forward()
方法传递到下一个 Stage
进行处理。并且这里使用的流水线是一个异步流水线,可以允许多个事件同时在里面运行处理,这种架构使得事件处理流程效率非常高。
那么我们的触摸事件从 NativePreImeInputStage
进入流水线后会经历什么处理过程呢:
我们并不是 IME
的事件,所以直接从 EarlyPostImeInputStage
开始:
EarlyPostImeInputStage
@Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); } } return FORWARD; }
第7行判断成立,进入 processPointerEvent()
:
private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; // Translate the pointer event for compatibility, if needed. if (mTranslator != null) { mTranslator.translateEventInScreenToAppWindow(event); } // Enter touch mode on down or scroll. final int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { ensureTouchMode(true); } // Offset the scroll position. if (mCurScrollY != 0) { event.offsetLocation(0, mCurScrollY); } // Remember the touch position for possible drag-initiation. if (event.isTouchEvent()) { mLastTouchPoint.x = event.getRawX(); mLastTouchPoint.y = event.getRawY(); mLastTouchSource = event.getSource(); } return FORWARD; }
对事件进行处理以后继续进入下一阶段。
NativePostImeInputStage
@Override protected int onProcess(QueuedInputEvent q) { if (mInputQueue != null) { mInputQueue.sendInputEvent(q.mEvent, q, false, this); return DEFER; } return FORWARD; }
如果有事件等待被处理,则推迟当前事件的处理(实现异步)。否则直接进入下一个阶段:
ViewPostImeInputStage
@Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q); } else { return processGenericMotionEvent(q); } } }
第7行判断成立,调用 processPointerEvent()
方法:
private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mAttachInfo.mUnbufferedDispatchRequested = false; final View eventTarget = (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ? mCapturingView : mView; mAttachInfo.mHandlingPointerEvent = true; boolean handled = eventTarget.dispatchPointerEvent(event); ... return handled ? FINISH_HANDLED : FORWARD; }
判断目标是否是 mCapturingView
,一般情况下目标就是 mView
(也就是当前 Window
的根 View
也就是 DecorView
),然后调用了它的 dispatchPointerEvent()
方法(继承自 View
):
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
到这里,我们终于看到了熟悉的 dispatchTouchEvent()
方法,同时这也是一般事件分发机制分析的开始。
小结
现在,我们了解了从原始事件的产生地点到某个应用 UI
线程事件循环再到根 view
的 dispatchTouchEvent()
的整个流程。分析这个过程还是要再次感谢 Gityuan的博客 ,这个过程找得到的资料只有他的文章,省了许多功夫。
下一篇文章开始就要讲解一般触摸事件分发分析的过程,也是参考资料比较多的部分。
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。