Android 4.2 Input 流程分析

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,InputReaderThread,InputDispatcherThread和一个 EventHubInterface . 因此输入事件处理流程跟此三位角色有关系.InputReaderThreadEventHubInterface抓取新的inputevent, 然后在依各个的eventtype 进入各个的 event mapper,  各个的 event mapper 处理输入事件完之后,InputReaderThread 将new input 放进 Queue 中. 之后 InputDispatcherThread再从Queue中取出Input Event 放入共享内存中. 此时再通知 View 去共享内存抓取 new Input Event, 取完之后再通知InputDispatcherThread是否可以再放新的InputEvent到共享内存中, 流程如下

首先先由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 函式中作了三件事

1. getEvents,

2. processEventsLocked,

3. 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 有

1. SwitchInputMapper,

2. VibratorInputMapper,

3. KeyboardInputMapper,

4. CursorInputMapper,

5. TouchInputMapper,

6. SingleTouchInputMapper,

7. MultiTouchInputMapper,

8.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;
}
InputDispatcherThread的启动流程开始分析到此为止,InputReader Thread 已经把新的Touch motion event放进InboundQueue了. 接下来就来看InputDispatcher Thread 的输入事件流程.

// 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 函数送 inputMessage.

世上万物常是一体两面, 在程序设计上也是如此, 因此有了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的判断值. 一收到inputevent马上分析为是 Key event type 还是 Motion event type. 由于我们这里是以 Motion event type 为例, 所以就分析 Motionevent 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来配置一块Motion Event 对象来使用. 为何要这么大费周章还要去回呼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 函数, 然后就送finishmessage 通知 InputDispatcherThread 之后, 就完成了这一个输入事件流程了. 然而, 依照过去写app的经验, 一般在app若要接收一些Touchevent 会有个 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 处理完 inputevent 之后, 再来分析 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);
}
 
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
 
    //检查outboundQueue中是否还有新的input event.
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;
 
        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
            // Publish the key event.
            status =
            connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction,
                    dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount,
                    keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
 
        case EventEntry::TYPE_MOTION: {
            MotionEntry* motionEntry =
                        static_cast<MotionEntry*>(eventEntry);
 
            PointerCoords scaledCoords[MAX_POINTERS];
            const PointerCoords* usingCoords = motionEntry->pointerCoords;
 
            // Set the X and Y offset depending on the input source.
            float xOffset, yOffset, scaleFactor;
              //do something
            } else {
                xOffset = 0.0f;
                yOffset = 0.0f;
                scaleFactor = 1.0f;
 
                // We don't want the dispatch target to know.
                if (dispatchEntry->targetFlags &
                  InputTarget::FLAG_ZERO_COORDS) {
                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
                        scaledCoords[i].clear();
                    }
                    usingCoords = scaledCoords;
                }
            }
 
            // Publish the motion event.
            //通知ViewRootImpl去共享内存抓取新的 input event
            status =
           connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source,
                    dispatchEntry->resolvedAction,
                    dispatchEntry->resolvedFlags,
                    motionEntry->edgeFlags, motionEntry->metaState,
                    motionEntry->buttonState,
                    xOffset, yOffset,
                    motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount,
                    motionEntry->pointerProperties,
                    usingCoords);
            break;
        }
 
        default:
            ALOG_ASSERT(false);
            return;
        }
 
        // Check the result.
        if (status) {
           if (status == WOULD_BLOCK) {
                if (connection->waitQueue.isEmpty()) {
                    abortBrokenDispatchCycleLocked(currentTime,
                         connection, true /*notify*/);
                } else {
                    // Pipe is full and we are waiting for the app to finish
                    //process some events
                    // before sending more events to it.
                    connection->inputPublisherBlocked = true;
                }
            } else {
                abortBrokenDispatchCycleLocked(currentTime, connection,
                                           true /*notify*/);
            }
            return;
        }
 
        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }  //while loop end
}
由此可知在 InputDispatcher 一收到InputConsumer对象送的 finish message之后, 就马上去呼叫 startDispatchCycleLocked 函数去检查 outboundQueue 里面还有没有新的inputevent. 若有的话就放入 InputMessage 共享内存, 然后通知 ViewRootImpl 去共享内存抓取新的 inputevent. 若没有新的 input event, 就不做事等待有新的inputevent进 outboundQueue.

没有更多推荐了,返回首页