Android 4.2 输入流程分析
2014-04-30 14:48
363 查看
Android 4.2 輸入流程研究心得
1. 前言Android中管理Input的兩個主要相關角色, 一是WindowManagerService, 一是跟View相關的ViewRootImpl. 基本原理方向從2.3到目前的4.2都一樣,在 Android
app一啟動之後, ViewRootImpl 就會先跟 WindowManagerService 建立inputChannel, 一旦 WindowManagerService 有收到 event 就會經由 inputChannel 通知 ViewRootImpl 去共享內存中抓取 event. 雖然方向一樣, 但是裡面的架構有改,目前最新的版本是android
4.2, 所以以下的輸入事件處理程序是以4.2來說明, 以後的版本一定會再更改.到時候在研究.
2. 事件處理程序
2.1 建立 InputChannel
Android Activity 一啟動時會先利用 setContentView 來設置畫面, 最後會由 ViewRootImpl 中的 setView函數來做畫面的設置,
setContentView 跟 setView 有甚麼關係, 並不是這裡的重點. 那是屬於Surface 的範圍. 接下來就來看 setView函數的實作, 跟輸入流程無關的程式碼就略掉.
//ViewRootImpl.java public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { // do something mInputChannel = new InputChannel(); // do something res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); //do something } } |
Session 繼承 IWindowSession , 因此 addToDisplay 函數必在 Session類別實作.
// Session.java @Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel); } |
// WindowManagerService.java public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { //do something String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); //do something } |
WindowManagerService 和 ViewRootImpl 之間就可以做輸入event的流程溝通. WindowManagerService -> ViewRootImpl, 這個方向是 WindowManagerService 通知ViewRootImpl 去共享內存去取input
event. ViewRootImpl -> WindowManagerService, 這個方向是 ViewRootImpl通知 WindowManagerService 已經處理完共享內存的 input event了, 請 WindowManagerService 在檢查是否有新的input
event, 若有的話就放入共享內存. 以下就來介紹 input event 流程.
2.2 輸入事件處理流程:
一開機的時候, SystemServer 會啟動 InputManagerService, 這時 InputManagerService 又會去啟動兩個Thread, InputReaderThread, InputDispatcherThread 和一個 EventHubInterface . 因此輸入事件處理流程跟此三位角色有關係. InputReaderThread 從 EventHubInterface 抓取新的input
event, 然後在依各個的event type 進入各個的 event mapper, 各個的 event mapper 處理輸入事件完之後, InputReaderThread 將new
input 放進 Queue中. 之後 InputDispatcherThread 再從Queue中取出Input
Event 放入共享內存中. 此時再通知 View 去共享內存抓取new Input Event, 取完之後再通知 InputDispatcherThread 是否可以再放新的Input
Event到共享內存中, 流程如下
首先先由InputReaderThread 啟動開始, 在android啟動 thread 是以下的形式.
// InputManager.cpp mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); |
// InputReader.cpp bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; } void InputReader::loopOnce() { //do something // 從 /dev/input下獲得新的Input event mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //依各個的event type 進入各個的 event mapper processEventsLocked(mEventBuffer, count); //通知 InputDispatcherThread 去處理new input. mQueuedListener->flush(); //do something } |
event, InputReader 是如何藉著 processEventsLocked 去做處理.
//InputReader.cpp void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { //do something int32_t deviceId = rawEvent->deviceId; //do something processEventsForDeviceLocked(deviceId, rawEvent, batchSize); //do something } void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { //do something InputDevice* device = mDevices.valueAt(deviceIndex); if (device->isIgnored()) { //ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; } device->process(rawEvents, count); } void InputDevice::process(const RawEvent* rawEvents, size_t count) { //do something for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { //do something for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); } //do something } //do something } |
VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper等等. 由於這些mapper處理的架構都差不多, 就拿 TouchInputMapper 來作分析
//InputReader.cpp void TouchInputMapper::process(const RawEvent* rawEvent) { // 處理以下的輸入事件, 並將各個的Touch value作相對應處理 mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { sync(rawEvent->when); } } void TouchInputMapper::sync(nsecs_t when) { //do something dispatchTouches(when, policyFlags); //do something } void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { //do something // No pointer id changes so this is a move event. // The listener takes care of batching moves so we don't have to deal with that // here. dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mCurrentCookedPointerData.pointerProperties, mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, currentIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); //do something } void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { //do something NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, action, flags, metaState, buttonState, edgeFlags, mViewport.displayId, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); getListener()->notifyMotion(&args); } InputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); } |
// InputListener.h Vector<NotifyArgs*> mArgsQueue; // InputListener.cpp void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); } |
//InputReader.cpp mQueuedListener->flush(); //InputListener.cpp void QueuedInputListener::flush() { size_t count = mArgsQueue.size(); for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; args->notify(mInnerListener); delete args; } mArgsQueue.clear(); } |
//InputListener.cpp void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyMotion(this); } |
而 InputDispatcher 繼承 InputDispatcherInterface類別, 所以 notifyMotion 函數必定實作在這類別中
// InputDispatcher.cpp void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { //do something // Policy: // - Ignore untrusted events and pass them along. // - No special filtering for injected events required at this time. // - Filter normal events based on screen state. // - For normal events brighten (but do not wake) the screen if currently dim. mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags); bool needWake; { // acquire lock mLock.lock(); if (shouldSendMotionToInputFilterLocked(args)) { mLock.unlock(); //initialize motion event for secondary display mLock.lock(); } // Just enqueue a new motion event. MotionEntry* newEntry = new MotionEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); } // release lock if (needWake) { mLooper->wake(); } } bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { // 將新的 input event 放進 InboundQueue bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); // do something return needWake; } |
Thread 的輸入事件流程.
同樣的從 InputDispatcherThread 的啟動流程開始分析
// InputDispatcher.cpp bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; } void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); mDispatcherIsAliveCondition.broadcast(); // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up //immediately. if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis); // 用來管理message queue, 負責接收 // 或發送 message. } void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { //do something // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { if (mInboundQueue.isEmpty()) { //do something } else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); } //do something // Get ready to dispatch the event. resetANRTimeoutsLocked(); //因為已經取到input event, 所以 reset //ANR timer. } //do something switch (mPendingEvent->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: //do something break; case EventEntry::TYPE_DEVICE_RESET: //do something break; case EventEntry::TYPE_KEY: //do something break; case EventEntry::TYPE_MOTION: MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); //do something done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; default: break; } if (done) { if (dropReason != DROP_REASON_NOT_DROPPED) { dropInboundEventLocked(mPendingEvent, dropReason); } releasePendingEventLocked(); *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up // immediately } } bool InputDispatcher::dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { 1. // Preprocessing. 2. // Clean up if dropping the event. 3. // support sending secondary display events to input monitors // Dispatch the motion. // do soemthing dispatchEventLocked(currentTime, entry, inputTargets); } void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { //do something for (size_t i = 0; i < inputTargets.size(); i++) { const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { //do something } } } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { 1. // Skip this event if the connection status is not normal. // We don't want to enqueue additional outbound events if the connection //is broken. // Split a motion event if needed. if (inputTarget->flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); //do something enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); //do something return; } } // Not splitting. Enqueue dispatch entries for the event as is. enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); } void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { bool wasEmpty = connection->outboundQueue.isEmpty(); // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.isEmpty()) { startDispatchCycleLocked(currentTime, connection); } } void InputDispatcher::enqueueDispatchEntryLocked( const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) { //do something // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this // connection. DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor); // Apply target flags and update the connection's input state. // Remember that we are waiting for this dispatch to complete. // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { // do something // 檢查outboundQueue 中是否有新的 input event. while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.isEmpty()) { // 從 outboundQueue 中取出新的 input event. DispatchEntry* dispatchEntry = connection->outboundQueue.head; // Publish the event. EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::TYPE_KEY: //do something break; case EventEntry::TYPE_MOTION: //do something // Publish the motion event. status = connection->inputPublisher.publishMotionEvent( // some argument. ); break; default: return; } } } //InputTransport.cpp status_t InputPublisher::publishMotionEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t action, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputMessage msg; msg.header.type = InputMessage::TYPE_MOTION; msg.body.motion.seq = seq; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.action = action; msg.body.motion.flags = flags; msg.body.motion.edgeFlags = edgeFlags; msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; for (size_t i = 0; i < pointerCount; i++) { msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } return mChannel->sendMessage(&msg); } 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); if (nWrite < 0) { int error = errno; if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN) { return DEAD_OBJECT; } return -error; } if (size_t(nWrite) != msgLength) { return DEAD_OBJECT; } return OK; } |
1. 從InboundQueue取 new input event 處理, 處理完放進 OutboundQueue
2. 從 OutboundQueue 取 new input event 處理, 處理完放進 inputMessage.
3. 利用::send 函數送 input Message.
世上萬物常是一體兩面, 在程式設計上也是如此, 因此有了sendMessage函數設計, 必有一個receiveMessage的函數. 所以有了 InputChannel::sendMessage 就會提供 InputChannel::receiveMessage 函數作接收message的處理. 搜尋了一下程式碼發現呼叫 InputChannel::receiveMessage 函數的地方是在InputConsumer::consume 函數中. 而這consume函數又是在哪被呼叫呢? 此流程牽扯到Thread 通訊課題, 在這不做詳細分析, 為了篇幅只能做簡單的說明, 在android中每一個Process都有一個looper, 此looper裡有一個messageQueue,
messageQueue中有很多來自此process中不同的thread所發出來的message. 管理這些message 就是這個名為 looper 的物件. 因此就去搜尋一下跟looper 相關的程式碼, 發現 android 有定義一個 LooperCallback 的抽象類別, 此抽象類別裡有一個pure
virtual function handleEvent函數, 因此只要找到誰繼承LooperCallback 類別, 就能找到 handleEvent 函數實作的地方. 最後在NativeInputEventReceiver 發現 handleEvent 函數實作足跡.
// android_view_InputEventReceiver.cpp int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { //do something status_t status = consumeEvents(env, false /*consumeBatches*/, -1); // do something } status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime) { //do something bool skipCallbacks = false; // 用來做為通知是否已完成dispach input event // 的一個判斷值. for (;;) { uint32_t seq; InputEvent* inputEvent; //果然在這裡發現了呼叫 InputConsumer::consume 函數的地方 status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); //do something if (!skipCallbacks) { jobject inputEventObj; switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: inputEventObj = android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); break; case AINPUT_EVENT_TYPE_MOTION: inputEventObj = android_view_MotionEvent_obtainAsCopy(env, static_cast<MotionEvent*>(inputEvent)); break; default: assert(false); // InputConsumer should prevent this from ever // happening inputEventObj = NULL; } if (inputEventObj) { env->CallVoidMethod(mReceiverObjGlobal, gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); if (env->ExceptionCheck()) { skipCallbacks = true; } } else { skipCallbacks = true; } } if (skipCallbacks) { mInputConsumer.sendFinishedSignal(seq, false); } } } |
variable skipCallbacks 當作一個是否完成dispatch input event的判斷值. 一收到input event馬上分析為是 Key event
type 還是 Motion event type. 由於我們這裡是以 Motion event type 為例, 所以就分析Motion event type 這個 case流程.
// android_view_InputEventReceiver.cpp status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime) { //do something case AINPUT_EVENT_TYPE_MOTION: inputEventObj = android_view_MotionEvent_obtainAsCopy(env, static_cast<MotionEvent*>(inputEvent)); break; //do something } // android_view_MotionEvent.cpp jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) { // 回呼 MotionEvent Java Layer 配置一塊 MotionEvent type 的 // 物件. jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain); // eventObj exception handler // 從MotionEvent 的java layer 讀取MotionEvent 物件指標值. MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj); if (!destEvent) { destEvent = new MotionEvent(); android_view_MotionEvent_setNativePtr(env, eventObj, destEvent); } destEvent->copyFrom(event, true); return eventObj; } |
// android_view_InputEventReceiver.cpp status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime) { //do something //do something // 將上面所得到的MotionEvent 物件指標值, 在傳入 Java Layer 的 // 函數裡 env->CallVoidMethod(mReceiverObjGlobal, gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); //do something } //InputEventReceiver.java // Called from native code. @SuppressWarnings("unused") private void dispatchInputEvent(int seq, InputEvent event) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); } /** * Called when an input event is received. * The recipient should process the input event and then call {@link #finishInputEvent} * to indicate whether the event was handled. No new input events will be received * until {@link #finishInputEvent} is called. * * @param event The input event that was received. */ public void onInputEvent(InputEvent event) { finishInputEvent(event, false); } |
message 通知InputDispatcherThread 之後, 就完成了這一個輸入事件流程了. 然而, 依照過去寫app的經驗, 一般在app若要接收一些Touch
event 會有個 call back function onTouch 函數要實作, 方便app可以接收到Touch event 然後作相關的應用.然而剛剛分析的輸入事件的流程似乎沒有去呼叫 onTouch 函數, 這到底是怎麼一回事? 原因在於 InputEventReceiver這個類別, 這個類別是屬於 abstract 類別, 在java的語法中,
abstract 類別即使類別中的成員函數都有實作, 也是無法實體化的, 因此只能有abstract 類別的衍生類別才能實體化. 搜尋了一下程式碼發現了幾個abstract
InputEventReceiver類別的衍生類別, 跟InputEvent 處理有關的衍生類別就是 WindowInputEventReceiver , 原因如下, 再一開始ViewRootImpl在作 setView 時, 除了new 一個新的 InputChannel 物件之後, 又 new 了一個 WindowInputEventReceiver 物件. 此WindowInputEventReceiver 類別正好又 overwrite onInputEvent 函數, 因此可以大膽推測dispatchInputEvent 呼叫的 onInputEvent 函數, 會是此類別的 onInputEvent 函數, 就在從 WindowInputEventReceiver 中的 onInputEvent函數開始分析.
// ViewRootImpl.java // 此類別是定義在ViewRootImpl 類別中, 最前面又是掛 final, 在java的語法// 中代表此類別只能給 ViewRootImpl 類別中使用且無法被繼承. final class WindowInputEventReceiver extends InputEventReceiver { //data member @Override public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); } // member function } 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 = mFirstPendingInputEvent; if (last == null) { mFirstPendingInputEvent = q; } else { while (last.mNext != null) { last = last.mNext; } last.mNext = q; } if (processImmediately) { doProcessInputEvents(); //一般的input event都是即使的 } else { scheduleProcessInputEvents(); } } void doProcessInputEvents() { while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) { QueuedInputEvent q = mFirstPendingInputEvent; mFirstPendingInputEvent = q.mNext; q.mNext = null; mCurrentInputEvent = q; deliverInputEvent(q); } // We are done processing all input events that we can process right now // so we can clear the pending flag immediately. } private void deliverInputEvent(QueuedInputEvent q) { // do something if (q.mEvent instanceof KeyEvent) { deliverKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { deliverPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { deliverTrackballEvent(q); } else { deliverGenericMotionEvent(q); } } // do something } private void deliverPointerEvent(QueuedInputEvent q) { // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { finishInputEvent(q, false); return; } // Translate the pointer event for compatibility, if needed. // Enter touch mode on down or scroll. // Offset the scroll position. // Remember the touch position for possible drag-initiation. // Dispatch touch to view hierarchy. boolean handled = mView.dispatchPointerEvent(event); if (handled) { finishInputEvent(q, true); return; } // Pointer event was unhandled. finishInputEvent(q, false); } |
//View .java public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } // 主要是用來實作觸控的一般通用的功能, ex press, click, long // press etc. if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } |
-> ViewRootImpl, 接下來 ViewRootImpl 處理完 input event 之後,再來分析 ViewRootImpl -> WindowManagerService 這方向. 由前面的 deliverPointerEvent 函數分析中, 會發現都會解由 finishInputEvent 來完成這一次的輸入事件流程. 就由 finishInputEvent 函數開始分析
// ViewRootImpl.java private void finishInputEvent(QueuedInputEvent q, boolean handled) { //do something if (q.mReceiver != null) { q.mReceiver.finishInputEvent(q.mEvent, handled); } else { q.mEvent.recycleIfNeededAfterDispatch(); } //do something } // InputEventReceiver.cpp public final void finishInputEvent(InputEvent event, boolean handled) { //do something nativeFinishInputEvent(mReceiverPtr, seq, handled); //do something } // android_view_InputEventReceiver.cpp static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jint seq, jboolean handled) { sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); status_t status = receiver->finishInputEvent(seq, handled); // exception handler } status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { // do something status_t status = mInputConsumer.sendFinishedSignal(seq, handled); // exception handler } // InputTransport.cpp status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { // do something // Send finished signal for the last message in the batch. return sendUnchainedFinishedSignal(seq, handled); } status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::TYPE_FINISHED; msg.body.finished.seq = seq; msg.body.finished.handled = handled; return mChannel->sendMessage(&msg); } |
InputDispatcher 類別的 handleReceiveCallback 函數就會被觸發. 原因在於InputDispatcher 在初始化的時候有去做register
InputChannel 的動作, 在 register InputChannel時, 會在自己new 出來的 looper 物件上增加一個 callback
function handleReceiveCallback.
// InputDispatcher.cpp mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { // do something for (;;) { // do something status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); // do something d->finishDispatchCycleLocked(currentTime, connection, seq, handled); gotOne = true; } } // InputTransport.cpp status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { //do something status_t result = mChannel->receiveMessage(&msg); //do something *outSeq = msg.body.finished.seq; *outHandled = msg.body.finished.handled; return OK; } // InputDispatcher.cpp void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { //do something // Notify other system components and prepare to start the next dispatch cycle. onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); } void InputDispatcher::onDispatchCycleFinishedLocked( nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; } void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( CommandEntry* commandEntry) { //do something // Start the next dispatch cycle for this connection. startDispatchCycleLocked(now(), connection); } |
event. 若有的話就放入InputMessage 共享內存, 然後通知 ViewRootImpl 去共享內存抓取新的 input event. 若沒有新的 input
event, 就不做事等待有新的input event進 outboundQueue.
原文地址:http://www.eoeandroid.com/home.php?mod=space&uid=10407&do=blog&id=5070
相关文章推荐
- Android事件处理分析+Android事件处理 +Android输入事件流程
- 对“Android输入事件流程中的EventHub分析及源码演示”的补充
- Android事件处理分析+Android事件处理 +Android输入事件流程
- Android 4.2 Input 流程分析
- Android事件处理分析+Android事件处理 +Android输入事件流程
- Android输入事件流程中的EventHub分析及源码演示
- Android 4.2 Input 流程分析
- Android输入事件流程中的EventHub分析及源码演示
- Android 4.2 SetContentView 流程分析(一)
- Android输入事件流程中的EventHub分析及源码演示
- Android 4.2 SetContentView 流程分析(二)
- Android 4.2 SetContentView 流程分析(三)
- Android 4.2 setContentView 流程研究分析
- Android输入事件流程中的EventHub分析及源码演示
- Android输入事件流程中的EventHub分析及源码演示
- Android 4.2 Input 流程分析
- android源码分析——事件输入流程MotionEvent事件处理流程
- Android AudioPlayer 流程分析
- Android窗口管理分析(2):WindowManagerService窗口管理之Window添加流程
- 【转载】Android 中 View 绘制流程分析