android java层事件分发机制

                    android java层事件分发机制



        我们知道在系统启动的时候,SystemServer会启动WindowManagerService,而WindowManagerService在启动的时候就会通过InputManagerService来负责监控键盘消息。
这些键盘消息一般都是分发给当前激活的Activity窗口来处理的,因此,当前激活的Activity窗口在创建的时候,会到WindowManagerService中去注册一个接收键盘消息的通道,表明它要处理键盘消息,而当InputManager监控到有键盘消息时,就会分给给它处理。

         当前激活的Activity窗口不再处于激活状态时,它也会到WindowManagerService中去反注册之前的键盘消息接收通道,这样,InputManager就不会再把键盘消息分发给它来处理。 

         本文主要介绍当前激活的Activity窗口已经注册好了消息接受通道,这些按键或者触屏事件是如何一步一步的传递到当前的activity或者View中的。而且本文主要关注java层的处理,后续我们会介绍native层的事件读取以及分发,敬请期待。


step1:Looper.pollOnce

         这个函数定义在frameworks/base/libs/utils/Looper.cpp文件中。在Looper类中,会创建一个管道,当调用Looper类的pollOnce函数时,如果管道中没有内容可读,那么当前线程就会进入到空闲等待状态;当有事件发生时,InputReader就会往这个管道中写入新的内容,这样就会唤醒前面正在等待事件发生的线程。

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
				//do simething

        result = pollInner(timeoutMillis);
    }
}

          该函数调用Looper.pollInner做进一步的处理。

step2:Looper.pollInner


该函数定义在system\core\libutils\Looper.cpp中;这个函数的实现比较长:

int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif

    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
                this, mNextMessageUptime - now, timeoutMillis);
#endif
    }

    // Poll.
    int result = ALOOPER_POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

    // We are about to idle.
    mIdling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mIdling = false;

    // Acquire lock.
    mLock.lock();

    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
        result = ALOOPER_POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - timeout", this);
#endif
        result = ALOOPER_POLL_TIMEOUT;
        goto Done;
    }

    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;

    // Invoke pending message callbacks.
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            // Remove the envelope from the list.
            // We keep a strong reference to the handler until the call to handleMessage
            // finishes.  Then we drop it so that the handler can be deleted *before*
            // we reacquire our lock.
            { // obtain handler
                sp handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();

#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
                ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                        this, handler.get(), message.what);
#endif
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = ALOOPER_POLL_CALLBACK;
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }

    // Release lock.
    mLock.unlock();

    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == ALOOPER_POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    return result;
}


下面我们分段进行分析。
    (1)利用epoll_wait函数来监听looper上的event,一旦有event发生,其eventCount就会回传event的数目。假若在最后一个参数指定的timeoutMillis内都没有事件发生, 其eventCount就会回传0, 若有发生错误回传-1。由 EPOLL_MAX_EVENTS 的定义值可以知道, 所监听到的event数最多为16个event。代码如下

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    (2)回调执行looper中所有request的函数handleEvent。代码如下


            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
     (3)此时大家就会有疑问了。response成员request的callback实例到底是什么呢? Server端的InputChannel注册完成后,把Client端的InputChannel转换成addWindow的参数outInputChannel中,然后返回到ViewRootImpl.setView函数中,继续执行Client端的InputChannel的注册过程,即为应用程序这一侧注册消息接收通道。代码如下:

                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

实例化了一个带有两个回调函数的WindowInputEventReceiver对象。这两个回调函数onInputEvent 和 onBatchedInputEventPending 与native 层的InputDispatcherThread 监听事件处理有关。



下面我们看与request的callback设置相关的代码,这些code在Looper.cpp 的函数addFd中;

       Request request;
       request.fd = fd;
       request.ident = ident;
       request.callback = callback;
       request.data = data;

       struct epoll_event eventItem;
       memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
       eventItem.events = epollEvents;
       eventItem.data.fd = fd;

       ssize_t requestIndex = mRequests.indexOfKey(fd);
       if (requestIndex < 0) {
           int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
           if (epollResult < 0) {
               ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
               return -1;
           }
           mRequests.add(fd, request);
       } else {
           int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
           if (epollResult < 0) {
               ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
               return -1;
           }
           mRequests.replaceValueAt(requestIndex, request);
       }

         此处我们发现,此初始化的流程正是注册Looper上的CallBack event 所带的信息的地方;也就是说先把request的information记录下来,等到要回调reponse的时候就知道要调哪一个request。
         由此可知, response.request.callback->handleEvent 等同于 (NativeInputEventReceiver*) receiver-> handleEvent。
      

        下面我们进一步分析NativeInputEventReceiver.handleEvnet的实现。


 Step 3. NativeInputEventReceiver. handleEvent

    该函数定义在android_view_InputEventReceiver.cpp中;

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
#if DEBUG_DISPATCH_CYCLE
        // This error typically occurs when the publisher has closed the input channel
        // as part of removing a window or finishing an IME session, in which case
        // the consumer will soon be disposed as well.
        ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                "events=0x%x", getInputChannelName(), events);
#endif
        return 0; // remove the callback
    }

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }

    if (events & ALOOPER_EVENT_OUTPUT) {
        for (size_t i = 0; i < mFinishQueue.size(); i++) {
            const Finish& finish = mFinishQueue.itemAt(i);
            status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
            if (status) {
                mFinishQueue.removeItemsAt(0, i);

                if (status == WOULD_BLOCK) {
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.",
                            getInputChannelName(), i, mFinishQueue.size());
#endif
                    return 1; // keep the callback, try again later
                }

                ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                        getInputChannelName(), status);
                if (status != DEAD_OBJECT) {
                    JNIEnv* env = AndroidRuntime::getJNIEnv();
                    String8 message;
                    message.appendFormat("Failed to finish input event.  status=%d", status);
                    jniThrowRuntimeException(env, message.string());
                    mMessageQueue->raiseAndClearException(env, "finishInputEvent");
                }
                return 0; // remove the callback
            }
        }
#if DEBUG_DISPATCH_CYCLE
        ALOGD("channel '%s' ~ Sent %u queued finish events; none left.",
                getInputChannelName(), mFinishQueue.size());
#endif
        mFinishQueue.clear();
        setFdEvents(ALOOPER_EVENT_INPUT);
        return 1;
    }

    ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
            "events=0x%x", getInputChannelName(), events);
    return 1;
}

该函数继续调用consumeEvents做进一步的处理。


 Step 4. NativeInputEventReceiver.consumeEvents

    该函数定义在android_view_InputEventReceiver.cpp中;


status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
            getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
#endif

    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (status) {
            if (status == WOULD_BLOCK) {
                if (!skipCallbacks && !mBatchedInputEventPending
                        && mInputConsumer.hasPendingBatch()) {
                    // There is a pending batch.  Come back later.
                    if (!receiverObj.get()) {
                        receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                        if (!receiverObj.get()) {
                            ALOGW("channel '%s' ~ Receiver object was finalized "
                                    "without being disposed.", getInputChannelName());
                            return DEAD_OBJECT;
                        }
                    }

                    mBatchedInputEventPending = true;
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
                            getInputChannelName());
#endif
                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
                    if (env->ExceptionCheck()) {
                        ALOGE("Exception dispatching batched input events.");
                        mBatchedInputEventPending = false; // try again later
                    }
                }
                return OK;
            }
            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
                    getInputChannelName(), status);
            return status;
        }
        assert(inputEvent);

        if (!skipCallbacks) {
            if (!receiverObj.get()) {
                receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                if (!receiverObj.get()) {
                    ALOGW("channel '%s' ~ Receiver object was finalized "
                            "without being disposed.", getInputChannelName());
                    return DEAD_OBJECT;
                }
            }

            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
#if DEBUG_DISPATCH_CYCLE
                ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
#endif
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast(inputEvent));
                break;

            case AINPUT_EVENT_TYPE_MOTION: {
#if DEBUG_DISPATCH_CYCLE
                ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
#endif
                MotionEvent* motionEvent = static_cast(inputEvent);
                if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
                    *outConsumedBatch = true;
                }
                inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
                break;
            }

            default:
                assert(false); // InputConsumer should prevent this from ever happening
                inputEventObj = NULL;
            }

            if (inputEventObj) {
#if DEBUG_DISPATCH_CYCLE
                ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
#endif
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
                skipCallbacks = true;
            }
        }

        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}

    函数consumeEvents主要做了两部分的工作;第一部分:将input event取出来。代码如下:

        uint32_t seq;
        InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);

          第二部分:将取出的input event传给java 层的 dispatchInputEvent 函数继续做分发处理

                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);

 Step 5. InputEventReceiver.dispatchInputEvent


    我们基于step4,继续分析按键消息的分发,该函数定义在文件InputEventReceiver.java中;终于消息从native层传递到了java层。

    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

dispatchInputEvent函数会继续将event传递给onInputEvent函数,然而此函数所属的InputEventReceiver是一个抽像类别,抽象类是无法实体化的,所以此函数最有可能是在抽像类所衍生的类中。InputEventReceiver类衍生的类是WindowInputEventReceiver类,所以就继续从WindowInputEventReceiver的onInputEvent函数分析。


Step 6. WindowInputEventReceiver. onInputEvent


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }

继续调用enqueueInputEvent 做进一步的处理。


 Step 7. ViewRootImpl.enqueueInputEvent


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

    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 = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

    该函数首先将event事件保存在QueuedInputEvent类型的q对象中;其次,由于函数enqueueInputEvent的实参processImmediately为true,因此会继续调用doProcessInputEvents进一步对按键消息进行处理。


Step 8. ViewRootImpl. doProcessInputEvents


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            deliverInputEvent(q);
        }

        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }


由于mNext 不为null,因此会继续调用ViewPreImeInputStage.deliver函数。


Step 13. ViewPreImeInputStage.deliver


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        /**
         * Delivers an event to be processed.
         */
        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q));
            }
        }

Step 14. ViewPreImeInputStage.onProcess


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        @Override
        protected int onProcess(QueuedInputEvent q) {
  
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            }
            return FORWARD;
        }

由于我们是以按键消息为示例,因此该函数会调用processKeyEvent做进一步的处理:

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;
            if (mView.dispatchKeyEventPreIme(event)) {
                return FINISH_HANDLED;
            }
            return FORWARD;
        }

该函数主要是在将按键消息分发给输入法之前,做一下预处理;这不是我们所关注的重点。一般情况下不做处理,即该函数返回FORWORD。
    因此,event事件就会像由nativePreImeStage阶段过渡到ViewPreImeInputStage阶段一样,通过onDeliverToNext被分发到下一阶段。后续我们就不详细描述每个阶段之间过渡的过程了,因为他们都是一样的。


Step 15. ImeInputStage.onProcess


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        protected int onProcess(QueuedInputEvent q) {
 
            if (mLastWasImTarget && !isInLocalFocusMode()) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null) {
                    final InputEvent event = q.mEvent;
                    if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
                    int result = imm.dispatchInputEvent(event, q, this, mHandler);
                    if (result == InputMethodManager.DISPATCH_HANDLED) {
                        return FINISH_HANDLED;
                    } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                        return FINISH_NOT_HANDLED;
                    } else {
                        return DEFER; // callback will be invoked later
                    }
                }
            }
            return FORWARD;
        }

该函数的主要目的是将event事件传递给InputMethodManager来处理这个按键事件,这不是我们关注的重点;我们假设该函数返回值为FORWORD。因此event事件会继续向下一个阶段传递。

Step 16. EarlyPostImeInputStage.onProcess


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        @Override
        protected int onProcess(QueuedInputEvent q) {

            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                }
            }
            return FORWARD;
        }

一般这个函数不做任何处理,直接返回FORWORD。继续分发该event事件。


Step 17. NativePostImeInputStage.onProcess


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null) {
                mInputQueue.sendInputEvent(q.mEvent, q, false, this);
                return DEFER;
            }
            return FORWARD;
        }

mInputQueue为null,继续分发event事件到下一个阶段。


Step 18. ViewPostImeInputStage.onProcess


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        @Override
        protected int onProcess(QueuedInputEvent q) {

            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

该函数调用processKeyEvent做进一步的处理。


Step 19. ViewPostImeInputStage.processKeyEvent


    该函数定义在文件\frameworks\base\core\java\android\view\ViewRootImpl.java中;

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

            if (event.getAction() != KeyEvent.ACTION_UP) {
                // If delivering a new key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
            }

            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }

            if (shouldDropInputEvent(q)) {
                return FINISH_NOT_HANDLED;
            }

            // If the Control modifier is held, try to interpret the key as a shortcut.
            if (event.getAction() == KeyEvent.ACTION_DOWN
                    && event.isCtrlPressed()
                    && event.getRepeatCount() == 0
                    && !KeyEvent.isModifierKey(event.getKeyCode())) {
                if (mView.dispatchKeyShortcutEvent(event)) {
                    return FINISH_HANDLED;
                }
                if (shouldDropInputEvent(q)) {
                    return FINISH_NOT_HANDLED;
                }
            }

            // Apply the fallback event policy.
            if (mFallbackEventHandler.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }
            if (shouldDropInputEvent(q)) {
                return FINISH_NOT_HANDLED;
            }

            // Handle automatic focus changes.
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                int direction = 0;
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        if (event.hasNoModifiers()) {
                            direction = View.FOCUS_LEFT;
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        if (event.hasNoModifiers()) {
                            direction = View.FOCUS_RIGHT;
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_UP:
                        if (event.hasNoModifiers()) {
                            direction = View.FOCUS_UP;
                        }
                        break;
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        if (event.hasNoModifiers()) {
                            direction = View.FOCUS_DOWN;
                        }
                        break;
                    case KeyEvent.KEYCODE_TAB:
                        if (event.hasNoModifiers()) {
                            direction = View.FOCUS_FORWARD;
                        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
                            direction = View.FOCUS_BACKWARD;
                        }
                        break;
                }
                if (direction != 0) {
                    View focused = mView.findFocus();
                    if (focused != null) {
                        View v = focused.focusSearch(direction);
                        if (v != null && v != focused) {
                            // do the math the get the interesting rect
                            // of previous focused into the coord system of
                            // newly focused view
                            focused.getFocusedRect(mTempRect);
                            if (mView instanceof ViewGroup) {
                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                        focused, mTempRect);
                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(
                                        v, mTempRect);
                            }
                            if (v.requestFocus(direction, mTempRect)) {
                                playSoundEffect(SoundEffectConstants
                                        .getContantForFocusDirection(direction));
                                return FINISH_HANDLED;
                            }
                        }

                        // Give the focused view a last chance to handle the dpad key.
                        if (mView.dispatchUnhandledMove(focused, direction)) {
                            return FINISH_HANDLED;
                        }
                    } else {
                        // find the best view to give focus to in this non-touch-mode with no-focus
                        View v = focusSearch(null, direction);
                        if (v != null && v.requestFocus(direction)) {
                            return FINISH_HANDLED;
                        }
                    }
                }
            }
            return FORWARD;
        }



这个函数是我们要分析的关键。首先调用mView.dispatchKeyEvent(event)将消息派发给根视图。对于应用程序而言,根视图就是PhoneWindow的DecorView对象:

            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }

Step 20. DecorView.dispatchKeyEvent


    该函数定义在文件\frameworks\base\policy\src\com\android\internal\policy\impl\ PhoneWindow.java中;

        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            final int keyCode = event.getKeyCode();
            final int action = event.getAction();
            final boolean isDown = action == KeyEvent.ACTION_DOWN;

            if (isDown && (event.getRepeatCount() == 0)) {
                // First handle chording of panel key: if a panel key is held
                // but not released, try to execute a shortcut in it.
                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
                    boolean handled = dispatchKeyShortcutEvent(event);
                    if (handled) {
                        return true;
                    }
                }

                // If a panel is open, perform a shortcut on it without the
                // chorded panel key
                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
                        return true;
                    }
                }
            }

            if (!isDestroyed()) {
                final Callback cb = getCallback();
                final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                        : super.dispatchKeyEvent(event);
                if (handled) {
                    return true;
                }
            }

            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
        }

      如果Callback不为null,该Callback对象一般是当前的activity,它实现了Window.Callback接口,即这里会调用activity实现的diapatchKeyEvent来做进一步的处理,它的主要工作就是:将该消息派发给拥有焦点的view组件处理,否则回调该event的dispatch() 函数,注意此时该event的第一个参数receiver为当前的activity实例。
    如果activity没有消耗掉该消息,则调用PhoneWindow中定义的onKeyDown或者onKeyUp对该消息进行最后的处理。这两个函数onKeyDown/onKeyUp主要是对系统的一些常用按键的默认处理,比如back/音量按键/相机按键等。
    具体这两个函数里面的实现我们在此就不分析了,虽然琐碎但是比较简单,感兴趣的同学可以看一下。


总结

        至此,android java 层分发消息给应用程序的过程就分析完成了,这是一个比较复杂的过程,不过,只要我们抓住主要的线索,就不难理解了。


       现在我们就小结一下这个过程的四个主要线索:
        A. 键盘事件发生,InputManager中的InputReader被唤醒;
        B. InputReader被唤醒后,它接着唤醒InputManager中的InputDispatcher;
        C. InputDispatcher被唤醒后,它接着唤醒应用程序的主线程来处理这个键盘事件;
        D. ViewRootImpl将按键消息的处理分成若干的阶段,分阶段进行处理。本文主要介绍的这一部分。




你可能感兴趣的:(移动开发,android,frameworks,native,epoll,app开发者)