http://developer.android.com/guide/topics/ui/ui-events.html

发布时间:2014-10-23 23:24:42
来源:分享查询网

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函数了。       执行完此函数,然后再一层一层向上返回。

返回顶部
查看电脑版