http://developer.android.com/guide/topics/ui/ui-events.html
Android KEY事件读取及分发(转)
1. 简介
WindowManagerService分发事件是通过它的InputManager来完成的。
在初始化时,各部分状态如下:
? InputManager.InputReader:正在睡眠等待事件的发生
? InputManager.InputDispatcher:正在等待InputReader从睡眠中醒过来并且唤醒它
? Activity应用程序:正在消息循环中等待InputDispatcher把它唤醒
初始化之后,如果有事件发生,其调用流程见下面的内容。
2. 事件分发流程
2.1 Server端
? InputReader
1) InputReader.pollOnce (InputReader.cpp)
被通知是否有事件可读
2) EventHub.getEvent (EventHub.cpp)
读取真正的事件
3) InputReader.process (InputReader.cpp)
4) InputReader.consumeEvent (InputReader.cpp)
5) InputDevice.process (InputReader.cpp)
6) mapper->process(rawEvent) (下面以键盘为例)
则真正调用: KeyboardInputMapper.process (InputReader.cpp)
7) KeyboardInputMapper.processKey (InputReader.cpp)
? InputDispatcher
8) InputDispatcher.notifyKey (InputDispatcher.cpp)
a) 生成KeyEvent (通过调用event.initialize)
b) 生成KeyEntry
c) 调用enqueueInboundEventLocked(newEntry),把KeyEntry加入到InputDispatcher类的mInboundQueue队列中
d) 根据需要唤醒InputDispatccherThread线程
9) InputDispatcher.dispatchOnce (InputDispatcher.cpp)
10) InputDispatcher.dispatchOnceInnerLocked (InputDispatcher.cpp)
从mInboundQueue队列中取出EventEntry
11) InputDispatcher.dispatchKeyLocked (InputDispatcher.cpp)
a) 从当前激活窗口mFocusedWindowHandle中获取InputWindowInfo
b) 从InputWindowInfo中获取inputChannel、frameLeft、frameTop并保存在 mCurrentInputTargets的top InputTarget成员中。后面InputDispatcher就会从mCurrentInputTargets中取出恰当的Activity窗口, 然后把键盘事件分发给它
c) 调用dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false)
12) InputDispatcher.dispatchEventToCurrentInputTargetsLocked (InputDispatcher.cpp)
[cpp]
- // InputDispatcher.cpp
- void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
- EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
-
- LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
-
- pokeUserActivityLocked(eventEntry);
-
- for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
- const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
-
- ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
- if (connectionIndex >= 0) {
-
- // 获取对应的Connection
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
- resumeWithAppendedMotionSample);
- } else {
-
- }
- }
- }
13) InputDispatcher.prepareDispatchCycleLocked (InputDispatcher.cpp)
14) InputDispatcher.enqueueDispatchEntriesLocked (InputDispatcher.cpp)
(1) InputDispatcher::enqueueDispatchEntryLocked (InputDispatcher.cpp)
a) 生成DispatchEntry
b) 把dispatchEntry加入connection->outboundQueue
(connection->outboundQueue.enqueueAtTail(dispatchEntry))
(2) InputDispatcher::startDispatchCycleLocked(以前队列空才执行)
15) InputDispatcher.startDispatchCycleLocked (InputDispatcher.cpp)
(1) 从connection->outboundQueue中取出DispatchEntry
(2) 对于EventEntry::TYPE_KEY,调用connection->inputPublisher.publishKeyEvent
或 对于EventEntry::TYPE_MOTION,调用connection->inputPublisher.publishMotionEvent
(3) InputPublisher.publishKeyEvent (InputTransport.cpp)
它把key event存入ashmem buffer中,即mSharedMessage->key中,
此ashmem buffer在InputDispatcher.registerInputChannel (connection->initialize)被创建。
(4) 发送dispath信号(即写发送pipe)给Activity应用程序
connection->inputPublisher.sendDispatchSignal-> InputChannel.sendSignal
[cpp]
- status_t InputPublisher::sendDispatchSignal() {
- mWasDispatched = true;
- return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
- }
至此,已经向Activity应用程序接收pipe中写入内容,则Activity应用程序的主线程就被唤醒了,开始处理此事件,即Activity应用程序上场了!
2.2 Client端 (Activity应用程序)
当应用程序的主线程因为这个InputChannel中的读管道被写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。
? NativeInputQueue
1) NativeInputQueue::handleReceiveCallback (android_view_InputQueue.cpp)
详细见容见下面的注释
[cpp]
- int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
-
- sp<Connection> connection;
- InputEvent* inputEvent;
- jobject inputHandlerObjLocal;
- jlong finishedToken;
- { // acquire lock
- AutoMutex _l(q->mLock);
-
- // 根据receiveFd获取 connectionIndex
- ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ...
- // 根据connectionIndex获取NativeInputQueue.Connection
- connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- ...
- // 从管道读取数据并检查是否与发送方写的一致
- status_t status = connection->inputConsumer.receiveDispatchSignal();
- ...
- // 获取NativeInputQueue.Connection中的InputConsumer对象,它与Server端的InputPublisher对应
- // InputConsumer::consume (InputTransport.cpp), 负责把事件读出来(MotionEvent或KeyEvent)并生成inputEventObj
- status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
- ...
-
- connection->messageInProgress = true;
- connection->messageSeqNum += 1;
-
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
-
- // 获取inputHandlerObjLocal对象
- // 在NativeInputQueue.registerInputChannel中Java传入,并保存在Connection中
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- } // release lock
-
- int32_t inputEventType = inputEvent->getType();
-
- jobject inputEventObj;
- jmethodID dispatchMethodId;
- switch (inputEventType) {
- case AINPUT_EVENT_TYPE_KEY:
-
- // 生成inputEventObj
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
-
- // 以指定inputHandlerObjLocal调用InputQueue.dispatchKeyEvent来处理此事件
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
- break;
-
- case AINPUT_EVENT_TYPE_MOTION:
- inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
- static_cast<MotionEvent*>(inputEvent));
- dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
- break;
-
- default:
- assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = NULL;
- }
-
- if (! inputEventObj) {
- LOGW("channel '%s' ~ Failed to obtain DVM event object.",
- connection->getInputChannelName());
- env->DeleteLocalRef(inputHandlerObjLocal);
- q->finished(env, finishedToken, false, false);
- return 1;
- }
-
- // 通知Java层的InputQueue来处理这个事件
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
-
- if (env->ExceptionCheck()) {
- LOGE("An exception occurred while invoking the input handler for an event.");
- LOGE_EX(env);
- env->ExceptionClear();
-
- q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
- }
-
- env->DeleteLocalRef(inputEventObj);
- env->DeleteLocalRef(inputHandlerObjLocal);
- return 1;
- }
? NativeInputQueue (下面以处理KeyEvent来讲)
2) InputQueue.dispatchKeyEvent (InputQueue.java)
[cpp]
- private static void dispatchKeyEvent(InputHandler inputHandler,
- KeyEvent event, long finishedToken) {
- FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
- // inputHandler是调用InputQueue.registerInputChannel时传进来的
- // 在ViewRootImpl.setView中调用
- // InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- // Looper.myQueue())
- inputHandler.handleKey(event, finishedCallback);
- }
mInputHandler的定义如下:
[cpp]
- private final InputHandler mInputHandler = new InputHandler() {
- public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
- dispatchKey(event, true);
- }
-
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
- dispatchMotion(event, true);
- }
- };
3) mInputHandler.handleKey (ViewRootImpl.java)
4) ViewRootImpl.dispatchKey (ViewRootImpl.java)
把KeyEvent封装成Message
5) ViewRootImpl.enqueueInputEvent
把Message转换为InputEventMessage消息,然后放于mPendingInputEvents链表尾。
ViewRootImpl不直接处理这个事件,而是把它作为一个消息(DISPATCH_KEY)放到消息队列中去处理,这个消息最后由ViewRootImpl类的deliverKeyEvent成员函数来处理
6) 在handleMessage中调用deliverKeyEvent
7) ViewRootImpl.deliverKeyEvent (ViewRootImpl.java)
[cpp]
- private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverTimeNanos = System.nanoTime();
- }
-
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 0);
- }
-
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- finishKeyEvent(event, sendDone, false);
- return;
- }
-
-
- // Perform predispatching before the IME.
- if (mView.dispatchKeyEventPreIme(event)) {
- finishKeyEvent(event, sendDone, true);
- return;
- }
-
- // InputMethodManager处理完这个键盘事件后,再回调用这里的
- // mInputMethodCallback对象的finishedEvent成员函数来把键盘
- // 事件分发给当前激活的Activity窗口处理。当然,在把这个键盘事件
- // 分发给InputMethodManager处理之前,ViewRoot也会先把这个键盘事
- // 件分发给当前激活的Activity窗口的dispatchKeyEventPreIme成员函数处理。
-
- // Dispatch to the IME before propagating down the view hierarchy.
- // The IME will eventually call back into handleFinishedEvent.
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- int seq = enqueuePendingEvent(event, sendDone);
- imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
- return;
- }
- }
-
- // Not dispatching to IME, continue with post IME actions.
- deliverKeyEventPostIme(event, sendDone);
- }
8) InputMethodCallack.finishedEvent (ViewRootImpl.java)
9) ViewRootImpl.dispatchFinishedEvent (ViewRootImpl.java)
发送FINISHED_EVENT到队列
10) ViewRootImpl.dispatchFinishedEvent (ViewRootImpl.java)
如果InputMethodManager没有处理这个键盘事件,那么ViewRoot就会把
这个键盘事件分发给当前激活的Activity窗口来处理。
11) ViewRootImpl.deliverKeyEventPostIme (ViewRootImpl.java)
12) mView.dispatchKeyEvent(event) (ViewRootImpl.java)
把事件分发给view hierarchy, mView为DecorView
13) DecorView.dispatchKeyEvent (PhoneWindow.java)
[cpp]
- public boolean dispatchKeyEvent(KeyEvent event) {
- final int keyCode = event.getKeyCode();
- final int action = event.getAction();
- final boolean isDown = action == KeyEvent.ACTION_DOWN;
-
- if (!isDestroyed()) {
- // 返回当前应用程序的激活的Activity窗口的Window.Callback接口,一般不为NULL
- // 因此,这个函数会调用Activity类的dispatchKeyEvent来处理这个键盘事件
- 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);
- }
14) Activity.dispatchKeyEvent (Activity.java)
[cpp]
- public boolean dispatchKeyEvent(KeyEvent event) {
- onUserInteraction();
- Window win = getWindow();
- if (win.superDispatchKeyEvent(event)) {
- return true;
- }
- View decor = mDecor;
- if (decor == null) decor = win.getDecorView();
- return event.dispatch(this, decor != null
- ? decor.getKeyDispatcherState() : null, this);
- }
Activity不是直接处理这个键盘事件,而是通过KeyEvent的dispatch转发一下。
注意,KeyEvent的成中函数dispatch的第一个参数的类型是KeyEvent.Callback,而Activity实现了这个接口,因此,这里可以传this引用过去。
15) KeyEvent.dispatch (KeyEvent.java)
根据一个键是按下(ACTION_DOWN)、还是松开(ACTION_UP) 或者是一个相同的键被多次按下和松开(ACTION_MULTIPLE)等不同事件类型来分别调用(receiver为 Activity)Activity的onKeyDown、onKeyLongPres、sonKeyUp和onKeyMultiple函数了。
执行完此函数,然后再一层一层向上返回。