当前位置: 代码迷 >> Android >> Android输入输出机制之前因后果之前生后世
  详细解决方案

Android输入输出机制之前因后果之前生后世

热度:63   发布时间:2016-05-01 19:23:42.0
Android输入输出机制之来龙去脉之前生后世
资源网站:
http://evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntu/
欢迎光临 www.51droidgame.com 网站进行讨论。上一篇介绍把top level的View的InputChannel其挂入到android系统中的机制给讲了,但是并没有解释如何驱动的,本篇就如何驱动来进行讲述。

先讲一下基本一般的输入处理方式的知识。一般的输入输出采用生产者,消费者模式,并构造队列进行处理,如下图

??

这种输入模型在android的系统中很多地方采用,先从最底层说起:

?为了由于触屏事件频率很高,android设计者讲一个循环线程,拆分为两级循环,并做了个队列来进行缓冲。


InputDispatcherThread和InputReaderThread。InputDispatcherThread在自己的循环中对InputReaderThread请求同步,InputReaderThread收到同步信号后,把事件放入InputDispatcher的队列中。

具体代码如下:

InputReader.cpp中有很多InputMapper,有SwitchInputMapper,KeyBoardInputMapper,TrackballInputMapper,SingleTouchInputMapper,

MultiTouchInputMapper。当线程从EventHub读取到Event后,调用这些InputMapper的pocess方法:

?

文件InputReader.cpp中:bool InputReaderThread::threadLoop() {    mReader->loopOnce();    return true;}void InputReader::loopOnce() {    RawEvent rawEvent;    mEventHub->getEvent(& rawEvent);#if DEBUG_RAW_EVENTS    LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",            rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,            rawEvent.value);#endif    process(& rawEvent);}

?process如下

? void InputReader::process(const RawEvent* rawEvent) {
??? switch (rawEvent->type) {
??? default:
??????? consumeEvent(rawEvent);
??????? break;
??? }
}

void InputReader::process(const RawEvent* rawEvent) {    switch (rawEvent->type) {        consumeEvent(rawEvent);        break;    }}

?

?

consumeEvent(rawEvent);

方法是关键,下面继续跟进;

?

void InputReader::consumeEvent(const RawEvent* rawEvent) {    int32_t deviceId = rawEvent->deviceId;    {         device->process(rawEvent);    } // release device registry reader lock}

?? device->process(rawEvent)行, 跟进去:

void InputDevice::process(const RawEvent* rawEvent) {    size_t numMappers = mMappers.size();    for (size_t i = 0; i < numMappers; i++) {        InputMapper* mapper = mMappers[i];        mapper->process(rawEvent);    }}

?下面进入了IputMapper,InputMapper是个纯虚类,process是个纯虚方法,随便找个例子跟进去:

?

void SingleTouchInputMapper::process(const RawEvent* rawEvent) {    switch (rawEvent->type) {    case EV_KEY:        switch (rawEvent->scanCode) {        case BTN_TOUCH:            mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;            mAccumulator.btnTouch = rawEvent->value != 0;            // Don't sync immediately.  Wait until the next SYN_REPORT since we might            // not have received valid position information yet.  This logic assumes that            // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.            break;        }        break;    case EV_SYN:        switch (rawEvent->scanCode) {        case SYN_REPORT:            sync(rawEvent->when);            break;        }        break;    }}

?最关键的是

 sync(rawEvent->when);

? 展开如下:

?

void SingleTouchInputMapper::sync(nsecs_t when) {    syncTouch(when, true); }

?

void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {       if (touchResult == DISPATCH_TOUCH) {        detectGestures(when);        dispatchTouches(when, policyFlags);    }}

?

?这两行,一个是虚拟键盘,一个是触摸屏。

????? TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);

dispatchTouches

? ? 只说触摸屏,虚拟键类似,触摸屏调用的是 ?

?

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {        // Dispatch pointer down events using the new pointer locations.        while (!downIdBits.isEmpty()) {            dispatchTouch(when, policyFlags, &mCurrentTouch,                    activeIdBits, downId, pointerCount, motionEventAction);        }    }}

?

dispatchTouch(when, policyFlags, &mCurrentTouch,                    activeIdBits, downId, pointerCount, motionEventAction);

这个方法展开如下:

?

void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,        TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,        int32_t motionEventAction) {    int32_t pointerIds[MAX_POINTERS];    PointerCoords pointerCoords[MAX_POINTERS];    int32_t motionEventEdgeFlags = 0;    float xPrecision, yPrecision;    {     getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,            motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,            pointerCount, pointerIds, pointerCoords,            xPrecision, yPrecision, mDownTime);}

?

? 这样就到了InputDiaptcher的notifyMotion方法,这个方法很长,都再处理MOVE事件,将无用的删除后,留下如下关键代码:

?

 void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,        uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,        uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,        float xPrecision, float yPrecision, nsecs_t downTime) {       // Just enqueue a new motion event.        MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,                deviceId, source, policyFlags, action, flags, metaState, edgeFlags,                xPrecision, yPrecision, downTime,                pointerCount, pointerIds, pointerCoords);        needWake = enqueueInboundEventLocked(newEntry);}

?最后一句:

 needWake = enqueueInboundEventLocked(newEntry);

?

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {    bool needWake = mInboundQueue.isEmpty();    mInboundQueue.enqueueAtTail(entry);    switch (entry->type) {    case EventEntry::TYPE_KEY: {        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);        if (isAppSwitchKeyEventLocked(keyEntry)) {            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {                mAppSwitchSawKeyDown = true;            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {                if (mAppSwitchSawKeyDown) {#if DEBUG_APP_SWITCH                    LOGD("App switch is pending!");#endif                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;                    mAppSwitchSawKeyDown = false;                    needWake = true;                }            }        }        break;    }    }    return needWake;}

?

mInboundQueue正是上面所说的队列。到此为止,从InputReader插入到队列就完成了。

?那么InputDispatcher又是如何从队列中取出来的呢?累了。

? InputDiapather的

dispatchOnce

方法如下:

void InputDispatcher::dispatchOnce() {    nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();    nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();    nsecs_t nextWakeupTime = LONG_LONG_MAX;    { // acquire lock        AutoMutex _l(mLock);        dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);        if (runCommandsLockedInterruptible()) {            nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately        }    } // release lock    // Wait for callback or timeout or wake.  (make sure we round up, not down)    nsecs_t currentTime = now();    int32_t timeoutMillis;    if (nextWakeupTime > currentTime) {        uint64_t timeout = uint64_t(nextWakeupTime - currentTime);        timeout = (timeout + 999999LL) / 1000000LL;        timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);    } else {        timeoutMillis = 0;    }    mLooper->pollOnce(timeoutMillis);}

?最关键的是

  dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);

    mLooper->pollOnce(timeoutMillis);//这个是个回调。
    

代码又长又臭

?

?

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,        nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {    case EventEntry::TYPE_MOTION: {        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {            dropReason = DROP_REASON_APP_SWITCH;        }        done = dispatchMotionLocked(currentTime, typedEntry,                &dropReason, nextWakeupTime);        break;    }}

?

dispatchMotionLocked

方法调用prepareDispatchCycleLocked,调用startDispatchCycleLocked,最终调用

?????? // Publish the key event.
??????? status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
??????????????? action, flags, keyEntry->keyCode, keyEntry->scanCode,
??????????????? keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
??????????????? keyEntry->eventTime);

或者 // Publish the motion event and the first motion sample.
??????? status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
??????????????? motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
??????????????? xOffset, yOffset,
??????????????? motionEntry->xPrecision, motionEntry->yPrecision,
??????????????? motionEntry->downTime, firstMotionSample->eventTime,
??????????????? motionEntry->pointerCount, motionEntry->pointerIds,
??????????????? firstMotionSample->pointerCoords);

?

??? 然后// Send the dispatch signal.
??? status = connection->inputPublisher.sendDispatchSignal();
??? if (status) {
??????? LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
??????????????? connection->getInputChannelName(), status);
??????? abortBrokenDispatchCycleLocked(currentTime, connection);
??????? return;
??? }

至此,InputDisapatcher也结束了。

?

既然发布出去,必然有订阅者:在InputTransport.cpp中

status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {#if DEBUG_TRANSPORT_ACTIONS    LOGD("channel '%s' consumer ~ consume",            mChannel->getName().string());#endif    *outEvent = NULL;    int ashmemFd = mChannel->getAshmemFd();    int result = ashmem_pin_region(ashmemFd, 0, 0);    if (result != ASHMEM_NOT_PURGED) {        if (result == ASHMEM_WAS_PURGED) {            LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "                    "which probably indicates that the publisher and consumer are out of sync.",                    mChannel->getName().string(), result, ashmemFd);            return INVALID_OPERATION;        }        LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",                mChannel->getName().string(), result, ashmemFd);        return UNKNOWN_ERROR;    }    if (mSharedMessage->consumed) {        LOGE("channel '%s' consumer ~ The current message has already been consumed.",                mChannel->getName().string());        return INVALID_OPERATION;    }    // Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal    // to the publisher that the message has been consumed (or is in the process of being    // consumed).  Eventually the publisher will reinitialize the semaphore for the next message.    result = sem_wait(& mSharedMessage->semaphore);    if (result < 0) {        LOGE("channel '%s' consumer ~ Error %d in sem_wait.",                mChannel->getName().string(), errno);        return UNKNOWN_ERROR;    }    mSharedMessage->consumed = true;    switch (mSharedMessage->type) {    case AINPUT_EVENT_TYPE_KEY: {        KeyEvent* keyEvent = factory->createKeyEvent();        if (! keyEvent) return NO_MEMORY;        populateKeyEvent(keyEvent);        *outEvent = keyEvent;        break;    }    case AINPUT_EVENT_TYPE_MOTION: {        MotionEvent* motionEvent = factory->createMotionEvent();        if (! motionEvent) return NO_MEMORY;        populateMotionEvent(motionEvent);        *outEvent = motionEvent;        break;    }    default:        LOGE("channel '%s' consumer ~ Received message of unknown type %d",                mChannel->getName().string(), mSharedMessage->type);        return UNKNOWN_ERROR;    }    return OK;}

?也许我们最关心的是如何订阅的,不得不取看一下JNI的代码,文件android_view_InputQueue.cpp

?聚焦到这里

?

status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,        jobject inputHandlerObj, jobject messageQueueObj) {    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,            inputChannelObj);    if (inputChannel == NULL) {        LOGW("Input channel is not initialized.");        return BAD_VALUE;    }#if DEBUG_REGISTRATION    LOGD("channel '%s' - Registered", inputChannel->getName().string());#endif    sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);    { // acquire lock        AutoMutex _l(mLock);        if (getConnectionIndex(inputChannel) >= 0) {            LOGW("Attempted to register already registered input channel '%s'",                    inputChannel->getName().string());            return BAD_VALUE;        }        uint16_t connectionId = mNextConnectionId++;        sp<Connection> connection = new Connection(connectionId, inputChannel, looper);        status_t result = connection->inputConsumer.initialize();        if (result) {            LOGW("Failed to initialize input consumer for input channel '%s', status=%d",                    inputChannel->getName().string(), result);            return result;        }        connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);        int32_t receiveFd = inputChannel->getReceivePipeFd();        mConnectionsByReceiveFd.add(receiveFd, connection);        looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);    } // release lock    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,            handleInputChannelDisposed, this);    return OK;}

?

?

? 也许更想知道的是消息队列在什么地方,进入InputQueue.java来看

?

    public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,            MessageQueue messageQueue) {        if (inputChannel == null) {            throw new IllegalArgumentException("inputChannel must not be null");        }        if (inputHandler == null) {            throw new IllegalArgumentException("inputHandler must not be null");        }        if (messageQueue == null) {            throw new IllegalArgumentException("messageQueue must not be null");        }                synchronized (sLock) {            if (DEBUG) {                Slog.d(TAG, "Registering input channel '" + inputChannel + "'");            }                        nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);        }    }    

?

在ViewRoot.java中有这么几行

?

                    InputQueue.registerInputChannel(mInputChannel, mInputHandler,                            Looper.myQueue());

?完毕。

这才牵涉到管道的问题,哪个Java中的Channel对应的正是linux系统的管道。有了管道,才能通过 跨进程方式回调回来,为什么是这个入口,上面进行了解释。具体参照INputQUEUE这个java类的JNI方法

int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data)

这个方法被InputQueue的RegisterInputChannel注册给了系统.系统通过回调,回调的是这个ALOOPER_EVENT_INPUT事件。

?

?

looper就是android中的【用户进程的循环】

注册的代码为 :

? looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

回调的java函数为

    回调的java代码的方法入口为:InputQueue.java中的。@SuppressWarnings("unused")    private static void dispatchMotionEvent(InputHandler inputHandler,            MotionEvent event, long finishedToken) {        Runnable finishedCallback = FinishedCallback.obtain(finishedToken);        inputHandler.handleMotion(event, finishedCallback);    }

?这样就回调到了ViewRoot中

?

?

欢迎光临 www.51droidgame.com 网站进行讨论。

1 楼 lenomon 2012-04-07  
这篇文章应该你能给增加些经验,Android事件传递机制【按键事件】
  相关解决方案