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

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)

 

// 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 

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)
     详细见容见下面的注释

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)
  

 

    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的定义如下:

    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)

    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)

          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)

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

 

 

 

 

 

 

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Android C 高级编程是指在Android开发中使用C语言进行高级编程的技术。而使用NDK(Native Development Kit)可以使开发者在Android应用中使用C/C++等本地语言进行编程。 NDK是一个工具集,它允许开发者在Android应用中嵌入本地代码,并且提供了一系列的开发工具和库,以便开发者能够在Android应用中使用C/C++进行高级编程。使用NDK可以提供更高的性能和更低的内存占用,适用于需要处理大量数据和高性能计算的应用场景。 在使用NDK进行Android C高级编程时,可以使用PDF(Portable Document Format)作为文档格式,以便对代码和项目进行更好的管理和文档化。在NDK的开发过程中,可以使用PDF文档记录关键的设计思路、代码逻辑、接口定义等信息,以方便团队协作和后续的维护。 使用NDK进行Android C高级编程的步骤大致如下: 1. 准备开发环境:安装NDK并配置好开发环境,包括设置NDK的路径和编译器等。 2. 创建新项目:使用Android Studio创建一个新的Android项目,并在项目中引入NDK的支持。 3. 编写C代码:使用C/C++语言编写需要调用的函数、算法或者数据结构等代码,并将其保存在适当的目录下。 4. 编写JNI接口:在生成的Java代码中,使用JNI(Java Native Interface)定义对应C代码的接口,以便在Java层调用C代码。 5. 编译和构建:使用NDK的工具集进行编译和构建,将C代码编译成适合Android平台使用的库文件(.so文件)。 6. 在Java代码中调用C代码:在需要调用C代码的地方,使用JNI接口调用对应的C函数,以实现和C代码的交互和调用。 使用PDF文档进行文档化可以帮助开发者更好地组织和管理代码、接口和设计文档等,方便后续的代码维护和项目协作。同时,也可以作为项目的参考文档,方便其他开发人员了解和使用项目。 ### 回答2: Android C 高级编程是针对使用NDK(Native Development Kit)的一种高级编程技术。NDK是Android开发工具包中的一个工具,允许开发者使用C、C++或其他本地编程语言编写Android应用程序的部分或全部代码。 使用NDK进行Android C高级编程有许多优点。首先,NDK提供了更高的性能和更好的控制权,特别是在处理图形、音频和计算密集型任务时。通过使用本地编程语言,开发者能够更好地利用底层系统资源,提高应用程序的执行效率和速度。 其次,NDK还提供了对现有C和C++库的支持。这意味着开发者可以使用许多已经存在的库和功能来加快开发进程。无需重新编写现有的代码,直接使用NDK与这些库进行集成即可。 在使用NDK进行Android C高级编程时,一种常见的用途是开发游戏。使用C或C++编写游戏代码可以获得更好的性能和更低的延迟,这对于游戏的流畅运行至关重要。 此外,开发者还可以使用NDK为现有的Java应用程序添加本地本地扩展。这样可以通过使用C或C++编写某些关键组件,以改进应用程序的性能或添加新的功能。 总的来说,通过使用NDK进行Android C高级编程,开发者可以获得更高的性能、更好的控制权和更好的资源利用。无论是开发游戏还是优化应用程序,使用NDK都是提高性能和扩展功能的好方法。通过阅读相关的PDF文档,开发者可以更深入地了解如何使用NDK进行Android C高级编程。 ### 回答3: Android NDK (Native Development Kit) 是一个用于开发 Android 应用程序的工具集,它使开发者能够使用 C 或 C++ 编写原生代码,并将其与 Java 编写的 Android 应用程序一起使用。使用 NDK 可以达到增加性能、复用现有的 C/C++ 代码以及访问底层硬件等目的。 在 Android C 高级编程中,使用 NDK 商用 PDF 库可以实现在 Android 应用程序中处理 PDF 文件的功能。PDF 文件是一种常见的电子文档格式,使用 PDF 库可以读取、编辑和生成 PDF 文件。 使用 NDK 进行 PDF 处理的一般步骤如下: 1. 集成 PDF 库:首先,需要将商用的 PDF 库 (.so 文件) 集成到 Android 项目中。可以通过在 Android.mk 文件中添加相关配置,确保 .so 文件正确地被编译和链接到应用程序中。 2. 创建 JNI 接口:为了在 Java 层与 C/C++ 层之间进行通信,需要创建 JNI (Java Native Interface) 接口。可以在创建 JNI 方法时使用 JNAerator 或者手动编写 JNI 代码,以便在 Java 层调用 C/C++ 的功能。 3. 对 PDF 文件进行处理:在 C/C++ 层,可以使用 PDF 库提供的功能来处理 PDF 文件。例如,可以使用库提供的函数来解析、渲染、添加标注、提取内容等。 4. 将数据返回给 Java 层:在 C/C++ 层处理完之后,可以通过 JNI 接口将处理后的数据返回给 Java 层。这样就可以在 Android 应用程序中显示或者存储处理后的 PDF 文件。 需要注意的是,在使用商用 PDF 库时,需要遵循相关的许可协议,并确保在开发和分发过程中合法使用该库。 总之,通过使用 NDK 和商用 PDF 库,可以使 Android 应用程序具有处理 PDF 文件的高级编程能力。同时,开发者需要具备 C/C++ 编程和 JNI 接口的使用经验,以便顺利地进行开发工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值