Input touch event 输入流程

http://www.eoeandroid.com/home.php?mod=space&uid=10407&do=blog&id=5070

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

        }

}

由此可知, new出來的 InputChannel 物件會傳入 mWindowSession  addToDisplay 函數中去查了一下mWindowSession 是由 IWindowSession 宣告一般 Ixxxx 的資料型態都是一個 Binder的介面通常功能實作都是會定義在由 Ixxxx 衍生的類別搜尋了一下程式碼, 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);

    }

 

InputChannel 物件 繼續傳入 WindowManagerService (mService 的資料型態是 WindowManagerService ) addWindow 函數.

// 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

}

之後就是利用 InputManagerService  registerInputChannel 函數將 WindowManagerService  ViewRootImpl之間的 InputChannel 建立起來一旦之間的通道建立起來, 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, InputReaderThreadInputDispatcherThread 和一個 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);

這時候InputReaderThread  threadLoop 函數就會被觸發.

// 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

}

 loopOnce 函式中作了三件事 getEvents, processEventsLocked, flush. 在這篇關心的是輸入事件處理流程所以就先來看從 EventHub 得到的 input 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

}

由此可知, InputReader 會利用一個mapper來管理所收到的 input event, 這些mapper  SwitchInputMapper, 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();

}

最後會發現會將所收到的Touch motion 資訊打包成message, 然後利用 QueuedInputListener  message 推入mArgsQueue 向量裡.

// InputListener.h

Vector<NotifyArgs*> mArgsQueue;

 

// InputListener.cpp

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {

    mArgsQueue.push(new NotifyMotionArgs(*args));

}

InputReader 處理完有關 Touch Motion input event之後便會把新的 Touch Motion input event 放進InboundQueue 流程如下:

//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();

}

在此會發現會用個loop來呼叫mArgsQueue vector中的 notify 函數繼續看下去

 

 

//InputListener.cpp

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {

    listener->notifyMotion(this);

}

在此發現notifyMotion 函數是由 InputListenerInterface 類別的物件所帶出來的由字串來看此類別只是當作是一個介面所以實作必定是其類別衍生的類別搜尋了一下整個程式碼發現 InputDispatcherInterface 繼承InputListenerInterface,

 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;

}

 

到此為止, InputReader Thread 已經把新的Touch motion event放進InboundQueue接下來就來看InputDispatcher 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;

}

上面呼叫了哪麼多層函數, InputDispatcherThread 就只做了以下幾件事:

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);

        }

 

    }

}

由這段程式碼可以發現當利用InputConsumer 物件收到由 InputDispatcherThread 送過來的 InputEvent message便會利用一個 boolean 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;

}

到此利用java layer來配置一塊 MotionEvent 物件來使用為何要這麼大費周章還要去回呼Java layer作配置物件的動作,推論應該是要藉由DVM GC來做回收未用到的記憶體接下來繼續分析後續動作

// 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);

}

 

到這裡先停一下若繼續分析下去會發現似乎直接就呼叫 finishInputEvent 函數然後就送finish 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或是無法掌握的input event就會被ignore掉不去處理.最後會去呼叫 View 類別的dispatchTouchEvent 函數.

//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;

    }

終於發現呼叫 onTouch 函數的地方了在這裡只要app有註冊 onTouchListener 介面onTouch 函數就會被觸發了到目前為止分析的方向是WindowManagerService -> 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);

}

 

一旦ViewRootImpl 藉由 InputChannel物件傳送 finish  message 這時

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);

}

由此可知在 InputDispatcher 一收到InputConsumer 物件送的 finish message之後就馬上去呼叫startDispatchCycleLocked 函數去檢查 outboundQueue 裡面還有沒有新的input event. 若有的話就放入InputMessage 共享內存然後通知 ViewRootImpl 去共享內存抓取新的 input event. 若沒有新的 input event, 就不做事等待有新的input event outboundQueue.

你可能感兴趣的:(Input touch event 输入流程)