Android应用程序键盘(Keyboard)消息处理机制分析(13)

   Step 18. InputDispatcher.registerInputChannel
        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {  
  2.     ......  
  3.   
  4.     { // acquire lock  
  5.         AutoMutex _l(mLock);  
  6.   
  7.         if (getConnectionIndexLocked(inputChannel) >= 0) {  
  8.             LOGW("Attempted to register already registered input channel '%s'",  
  9.                 inputChannel->getName().string());  
  10.             return BAD_VALUE;  
  11.         }  
  12.   
  13.         sp<Connection> connection = new Connection(inputChannel);  
  14.         status_t status = connection->initialize();  
  15.         if (status) {  
  16.             LOGE("Failed to initialize input publisher for input channel '%s', status=%d",  
  17.                 inputChannel->getName().string(), status);  
  18.             return status;  
  19.         }  
  20.   
  21.         int32_t receiveFd = inputChannel->getReceivePipeFd();  
  22.         mConnectionsByReceiveFd.add(receiveFd, connection);  
  23.   
  24.         if (monitor) {  
  25.             mMonitoringChannels.push(inputChannel);  
  26.         }  
  27.   
  28.         mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);  
  29.   
  30.         runCommandsLockedInterruptible();  
  31.     } // release lock  
  32.     return OK;  
  33. }  

        这个函数首先会通过getConnectionIndexLocked检查从参数传进来的InputChannel是否已经注册过了,如果已经注册过了,就返回一个BAD_VALUE值了,否则的话,就会创建一个Connection对象来封装即将要注册的inputChannel,我们可以不关心这个Connection对象的实现,接着还通过调用inputChannel->getReceivePipeFd获得一个管
道的读端描述符。回忆一下Step 14中的InputChannel.openInputChannelPair函数,我们创建了一个Server端的InputChannel,就是对应这里的inputChannel了,这个inputChannel的Receive Pipe Fd就是我们前面说的反向管道的读端描述符了。有了这个Receive Pipe Fd后,就以它作为Key值来把前面创建的Connection对象保存在InputDispatcher中,这样就基本完成键盘消息接收通道的注册了。但是,注册的工作还未完成,最后,还要把这个Receive Pipe Fd添加到InputDispatcher的成员变量mLooper中去,这里的成员变量mLooper的类型为Looper,我们在前面介绍InputManager的启动过程的Step 15中已经见过了,这里就不再详述了,不过这里仍然值得介绍一下它的addFd函数。

 

        在前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析中,我们在介绍到Android应用程序的消息循环一节时,曾经说过,在Looper类内部,会创建一个管道,然后Looper会睡眠在这个管道的读端,等待另外一个线程来往这个管道的写端写入新的内容,从而唤醒等待在这个管道读端的线程,除此之外,Looper还可以同时睡眠等待在其它的文件描述符上,因为它是通过Linux系统的epoll机制来批量等待指定的文件有新的内容可读的。这些其它的文件描述符就是通过Looper类的addFd成函数添加进去的了,在添加的时候,还可以指定回调函数,即当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个hanldeReceiveCallback函数,有兴趣的读者可以自己研究一下Looper类的addFd函数的实现,它位于frameworks/base/libs/utils/Looper.cpp文件中。

        分析到这里,Server端的InputChannel就注册完成了。回忆一下前面介绍InputManager启动过程的Step 14,这时InputDispatcherThread同时睡眠在InputDispatcher的成员变量mLooper内部的管道的读端以及这里的Server端InputChannel里面的反向管道的读端上,mLooper内部的管道的读端等待键盘事件的发生而被唤醒,而Server端InputChannel里面的反向管道的读端等待Client端InputChannel里面的反向管道的写端被写入新的内容而被唤醒。

        Server端的InputChannel注册完成后,回到Step 11中的WindowManagerService.addWindow函数,接下来就是把Client端的InputChannel转换成addWindow的参数outInputChannel中,然后返回到Step 1中的ViewRoot.setView函数中,继续执行Client端的InputChannel的注册过程,即为应用程序这一侧注册键盘消息接收通道:

  1. if (view instanceof RootViewSurfaceTaker) {  
  2.     mInputQueueCallback =  
  3.         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();  
  4. }  
  5. if (mInputQueueCallback != null) {  
  6.     mInputQueue = new InputQueue(mInputChannel);  
  7.     mInputQueueCallback.onInputQueueCreated(mInputQueue);  
  8. else {  
  9.     InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  10.         Looper.myQueue());  
  11. }  

 

        这里的变量view一般不为RootViewSurfaceTaker的实例,因此,最后会执行下面语句:

  1. InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  2.     Looper.myQueue());  

        它调用InputQueue的registerInputChannel函数为应用程序注册键盘消息接收通道,这里的mInputChannel即为我们在前面Step 14中创建的Client端的InputChannel;Looper.myQueue函数返回的便是应用程序主线程的消息队列,具体可以参考前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析;参数mInputHandler是一个回调对象,当有键盘事件发生时,这个mInputHandler的handleKey函数就会被调用,在后面的分析中,我们将会看到。

 

        Step 19. InputQueue.registerInputChannel

        这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中:

  1. public final class InputQueue {  
  2.     ......  
  3.   
  4.     public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,  
  5.             MessageQueue messageQueue) {  
  6.         ......  
  7.   
  8.         synchronized (sLock) {  
  9.             ......  
  10.   
  11.             nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  

         这个函数调用本地方法nativeRegisterInputChannel函数来执行进一步的操作。

 

         Step 20. InputQueue.nativeRegisterInputChannel

         这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) {  
  3.     status_t status = gNativeInputQueue.registerInputChannel(  
  4.        env, inputChannelObj, inputHandlerObj, messageQueueObj);  
  5.   
  6.     ......  
  7. }  

        这里继续调用NativeInputQueue的registerInputChannel函数来执行真正的键盘消息接收通道的工作。

 

        Step 21. NativeInputQueue.registerInputChannel

        这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,  
  2.         jobject inputHandlerObj, jobject messageQueueObj) {  
  3.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  4.         inputChannelObj);  
  5.     ......  
  6.   
  7.     sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);  
  8.   
  9.     { // acquire lock  
  10.         AutoMutex _l(mLock);  
  11.   
  12.         if (getConnectionIndex(inputChannel) >= 0) {  
  13.             LOGW("Attempted to register already registered input channel '%s'",  
  14.                 inputChannel->getName().string());  
  15.             return BAD_VALUE;  
  16.         }  
  17.   
  18.         uint16_t connectionId = mNextConnectionId++;  
  19.         sp<Connection> connection = new Connection(connectionId, inputChannel, looper);  
  20.         status_t result = connection->inputConsumer.initialize();  
  21.         if (result) {  
  22.             LOGW("Failed to initialize input consumer for input channel '%s', status=%d",  
  23.                 inputChannel->getName().string(), result);  
  24.             return result;  
  25.         }  
  26.   
  27.         connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);  
  28.   
  29.         int32_t receiveFd = inputChannel->getReceivePipeFd();  
  30.         mConnectionsByReceiveFd.add(receiveFd, connection);  
  31.   
  32.         looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);  
  33.     } // release lock  
  34.   
  35.     ......  
  36.     return OK;  
  37. }  

        这里注册应用程序的InputChannel的逻辑和前面介绍的Step 18中在InputDispatcher中注册Server端的InputChannel是一样的,所不同的是,这里用的looper是应用程序主线程中的消息循环对象Looper,而添加到这个looper对象中的Receive Pipe Fd是前面在Step 14中创建的前向管道的读端文件描述符,而使用的回调函数是NativeInputQueue的成员函数handleReceiveCallback。

 

        介绍到这里,应用程序注册键盘消息接收通道的过程就分析完成了。这个过程比较复杂,这里小结一下:

        A. 即将会被激活的Activity窗口,会通知InputManager,它是当前激活的窗口,因此,一旦发生键盘事件的时候,InputManager就把这个键盘事件抛给这个Activity处理;

        B. 应用程序会为这个Activity窗口和InputManager之间创建一个键盘消息接收通道,这个通道的一端由一个Server端的InputChannel构成,另一端由Client端的InputChannel构成,Server端的InputChannel注册在由InputManager所管理的InputDispatcher中,而Client端的InputChannel注册在由应用程序主线程的消息循环对象Looper中;

        C. 注册在InputDispatcher中的InputChannel由一个反向管道的读端和一个前向管道的写端组成,而注册在应用程序主线程的消息循环对象Looper中的InputChannel由这个前向管道的读端和反向管道的写端组成,这种交叉结构使得当有键盘事件发生时,InputDispatcher可以把这个事件通知给应用程序。

        应用程序注册好键盘消息接收通道后,接下来就开始分析InputManager分发键盘消息给应用程序的过程了。

你可能感兴趣的:(android,keyboard,消息处理机制分析)