Android系统--事件读取及分发

来源:互联网 发布:csgo武器数据 编辑:程序博客网 时间:2024/06/11 19:26

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] view plaincopy
  1. // InputDispatcher.cpp  
  2. void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,  
  3.         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {  
  4.   
  5.     LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true  
  6.   
  7.     pokeUserActivityLocked(eventEntry);  
  8.   
  9.     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  
  10.         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  
  11.   
  12.         ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  
  13.         if (connectionIndex >= 0) {  
  14.   
  15.             // 获取对应的Connection  
  16.             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  17.   
  18.             prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  
  19.                     resumeWithAppendedMotionSample);  
  20.         } else {  
  21.   
  22.         }  
  23.     }  
  24. }  

 

  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] view plaincopy
  1. status_t InputPublisher::sendDispatchSignal() {  
  2.     mWasDispatched = true;  
  3.     return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);  
  4. }  


      至此,已经向Activity应用程序接收pipe中写入内容,则Activity应用程序的主线程就被唤醒了,开始处理此事件,即Activity应用程序上场了!

2.2 Client端 (Activity应用程序)

      当应用程序的主线程因为这个InputChannel中的读管道被写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。

   • NativeInputQueue 
   1) NativeInputQueue::handleReceiveCallback (android_view_InputQueue.cpp) 
     详细见容见下面的注释

[cpp] view plaincopy
  1. int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {  
  2.     NativeInputQueue* q = static_cast<NativeInputQueue*>(data);  
  3.     JNIEnv* env = AndroidRuntime::getJNIEnv();  
  4.   
  5.     sp<Connection> connection;  
  6.     InputEvent* inputEvent;  
  7.     jobject inputHandlerObjLocal;  
  8.     jlong finishedToken;  
  9.     { // acquire lock  
  10.         AutoMutex _l(q->mLock);  
  11.   
  12.         // 根据receiveFd获取 connectionIndex  
  13.         ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  14.          ...  
  15.         // 根据connectionIndex获取NativeInputQueue.Connection  
  16.         connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);  
  17.         ...  
  18.         // 从管道读取数据并检查是否与发送方写的一致  
  19.         status_t status = connection->inputConsumer.receiveDispatchSignal();  
  20.          ...  
  21.         // 获取NativeInputQueue.Connection中的InputConsumer对象,它与Server端的InputPublisher对应  
  22.         // InputConsumer::consume (InputTransport.cpp), 负责把事件读出来(MotionEvent或KeyEvent)并生成inputEventObj  
  23.         status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);  
  24.         ...         
  25.   
  26.         connection->messageInProgress = true;  
  27.         connection->messageSeqNum += 1;  
  28.   
  29.         finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);  
  30.    
  31.         // 获取inputHandlerObjLocal对象  
  32.         // 在NativeInputQueue.registerInputChannel中Java传入,并保存在Connection中  
  33.         inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);  
  34.     } // release lock  
  35.   
  36.     int32_t inputEventType = inputEvent->getType();  
  37.   
  38.     jobject inputEventObj;  
  39.     jmethodID dispatchMethodId;  
  40.     switch (inputEventType) {  
  41.     case AINPUT_EVENT_TYPE_KEY:  
  42.   
  43.         // 生成inputEventObj  
  44.         inputEventObj = android_view_KeyEvent_fromNative(env,  
  45.                 static_cast<KeyEvent*>(inputEvent));  
  46.           
  47.         // 以指定inputHandlerObjLocal调用InputQueue.dispatchKeyEvent来处理此事件   
  48.         dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;  
  49.         break;  
  50.   
  51.     case AINPUT_EVENT_TYPE_MOTION:  
  52.         inputEventObj = android_view_MotionEvent_obtainAsCopy(env,  
  53.                 static_cast<MotionEvent*>(inputEvent));  
  54.         dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;  
  55.         break;  
  56.   
  57.     default:  
  58.         assert(false); // InputConsumer should prevent this from ever happening  
  59.         inputEventObj = NULL;  
  60.     }  
  61.   
  62.     if (! inputEventObj) {  
  63.         LOGW("channel '%s' ~ Failed to obtain DVM event object.",  
  64.                 connection->getInputChannelName());  
  65.         env->DeleteLocalRef(inputHandlerObjLocal);  
  66.         q->finished(env, finishedToken, falsefalse);  
  67.         return 1;  
  68.     }  
  69.       
  70.     // 通知Java层的InputQueue来处理这个事件  
  71.     env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,  
  72.             dispatchMethodId, inputHandlerObjLocal, inputEventObj,  
  73.             jlong(finishedToken));  
  74.   
  75.     if (env->ExceptionCheck()) {  
  76.         LOGE("An exception occurred while invoking the input handler for an event.");  
  77.         LOGE_EX(env);  
  78.         env->ExceptionClear();  
  79.   
  80.         q->finished(env, finishedToken, falsetrue /*ignoreSpuriousFinish*/);  
  81.     }  
  82.   
  83.     env->DeleteLocalRef(inputEventObj);  
  84.     env->DeleteLocalRef(inputHandlerObjLocal);  
  85.     return 1;  
  86. }  


   • NativeInputQueue (下面以处理KeyEvent来讲)
   2) InputQueue.dispatchKeyEvent (InputQueue.java)
  

 

[cpp] view plaincopy
  1. private static void dispatchKeyEvent(InputHandler inputHandler,  
  2.         KeyEvent event, long finishedToken) {  
  3.     FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);  
  4.     // inputHandler是调用InputQueue.registerInputChannel时传进来的  
  5.     // 在ViewRootImpl.setView中调用  
  6.     // InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  7.     //                        Looper.myQueue())  
  8.     inputHandler.handleKey(event, finishedCallback);  
  9. }  

         mInputHandler的定义如下:

[cpp] view plaincopy
  1. private final InputHandler mInputHandler = new InputHandler() {  
  2.     public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {  
  3.         startInputEvent(finishedCallback);  
  4.         dispatchKey(event, true);  
  5.     }  
  6.   
  7.     public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {  
  8.         startInputEvent(finishedCallback);  
  9.         dispatchMotion(event, true);  
  10.     }  
  11. };  

 


   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] view plaincopy
  1. private void deliverKeyEvent(KeyEvent event, boolean sendDone) {  
  2.     if (ViewDebug.DEBUG_LATENCY) {  
  3.         mInputEventDeliverTimeNanos = System.nanoTime();  
  4.     }  
  5.   
  6.     if (mInputEventConsistencyVerifier != null) {  
  7.         mInputEventConsistencyVerifier.onKeyEvent(event, 0);  
  8.     }  
  9.   
  10.     // If there is no view, then the event will not be handled.  
  11.     if (mView == null || !mAdded) {  
  12.         finishKeyEvent(event, sendDone, false);  
  13.         return;  
  14.     }  
  15.   
  16.   
  17.     // Perform predispatching before the IME.  
  18.     if (mView.dispatchKeyEventPreIme(event)) {  
  19.         finishKeyEvent(event, sendDone, true);  
  20.         return;  
  21.     }  
  22.   
  23.     // InputMethodManager处理完这个键盘事件后,再回调用这里的  
  24.     // mInputMethodCallback对象的finishedEvent成员函数来把键盘  
  25.     // 事件分发给当前激活的Activity窗口处理。当然,在把这个键盘事件  
  26.     // 分发给InputMethodManager处理之前,ViewRoot也会先把这个键盘事  
  27.     // 件分发给当前激活的Activity窗口的dispatchKeyEventPreIme成员函数处理。   
  28.   
  29.     // Dispatch to the IME before propagating down the view hierarchy.  
  30.     // The IME will eventually call back into handleFinishedEvent.  
  31.     if (mLastWasImTarget) {  
  32.         InputMethodManager imm = InputMethodManager.peekInstance();  
  33.         if (imm != null) {  
  34.             int seq = enqueuePendingEvent(event, sendDone);  
  35.             imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);  
  36.             return;  
  37.         }  
  38.     }  
  39.   
  40.     // Not dispatching to IME, continue with post IME actions.  
  41.     deliverKeyEventPostIme(event, sendDone);  
  42. }       


    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] view plaincopy
  1.   public boolean dispatchKeyEvent(KeyEvent event) {  
  2.     final int keyCode = event.getKeyCode();  
  3.     final int action = event.getAction();  
  4.     final boolean isDown = action == KeyEvent.ACTION_DOWN;  
  5.   
  6.     if (!isDestroyed()) {  
  7.         // 返回当前应用程序的激活的Activity窗口的Window.Callback接口,一般不为NULL  
  8.         // 因此,这个函数会调用Activity类的dispatchKeyEvent来处理这个键盘事件  
  9.         final Callback cb = getCallback();  
  10.   
  11.         final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)  
  12.                 : super.dispatchKeyEvent(event);  
  13.         if (handled) {  
  14.             return true;  
  15.         }  
  16.     }  
  17.   
  18.     return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)  
  19.             : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);  
  20. }     

    14) Activity.dispatchKeyEvent (Activity.java)

[cpp] view plaincopy
  1. public boolean dispatchKeyEvent(KeyEvent event) {  
  2.       onUserInteraction();  
  3.       Window win = getWindow();  
  4.       if (win.superDispatchKeyEvent(event)) {  
  5.           return true;  
  6.       }  
  7.       View decor = mDecor;  
  8.       if (decor == null) decor = win.getDecorView();  
  9.       return event.dispatch(this, decor != null  
  10.               ? decor.getKeyDispatcherState() : null, this);  
  11.   }  

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

原创粉丝点击