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

  Step 12. InputChannel.openInputChannelPair

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

  1. public final class InputChannel implements Parcelable {  
  2.     ......  
  3.   
  4.     /** 
  5.     * Creates a new input channel pair.  One channel should be provided to the input 
  6.     * dispatcher and the other to the application's input queue. 
  7.     * @param name The descriptive (non-unique) name of the channel pair. 
  8.     * @return A pair of input channels.  They are symmetric and indistinguishable. 
  9.     */  
  10.     public static InputChannel[] openInputChannelPair(String name) {  
  11.         ......  
  12.   
  13.         return nativeOpenInputChannelPair(name);  
  14.     }  
  15.   
  16.     ......  
  17. }  

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

 

         Step 13. InputChannel.nativeOpenInputChannelPair
         这个函数定义在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:

  1. static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,  
  2.         jclass clazz, jstring nameObj) {  
  3.      const char* nameChars = env->GetStringUTFChars(nameObj, NULL);  
  4.      String8 name(nameChars);  
  5.      env->ReleaseStringUTFChars(nameObj, nameChars);  
  6.   
  7.      sp<InputChannel> serverChannel;  
  8.      sp<InputChannel> clientChannel;  
  9.      status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);  
  10.   
  11.      if (result) {  
  12.          LOGE("Could not open input channel pair.  status=%d", result);  
  13.          jniThrowRuntimeException(env, "Could not open input channel pair.");  
  14.          return NULL;  
  15.      }  
  16.   
  17.      // TODO more robust error checking  
  18.      jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,  
  19.          new NativeInputChannel(serverChannel));  
  20.      jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,  
  21.          new NativeInputChannel(clientChannel));  
  22.   
  23.      jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);  
  24.      env->SetObjectArrayElement(channelPair, 0, serverChannelObj);  
  25.      env->SetObjectArrayElement(channelPair, 1, clientChannelObj);  
  26.      return channelPair;  
  27. }  

        这个函数根据传进来的参数name在C++层分别创建两个InputChannel,一个作为Server端使用,一个作为Client端使用,这里的Server端即是指InputManager,而Client端即是指应用程序。这两个本地的InputChannel是通过InputChannel::openInputChannelPair函数创建的,创建完成后,再相应地在Java层创建相应的两个InputChannel,然后返回。

 

        Step 14. InputChannel.openInputChannelPair
        这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputChannel::openInputChannelPair(const String8& name,  
  2.         sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {  
  3.     status_t result;  
  4.   
  5.     int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);  
  6.     if (serverAshmemFd < 0) {  
  7.         ......  
  8.     } else {  
  9.         result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);  
  10.         if (result < 0) {  
  11.             ......  
  12.         } else {  
  13.             // Dup the file descriptor because the server and client input channel objects that  
  14.             // are returned may have different lifetimes but they share the same shared memory region.  
  15.             int clientAshmemFd;  
  16.             clientAshmemFd = dup(serverAshmemFd);  
  17.             if (clientAshmemFd < 0) {  
  18.                 ......  
  19.             } else {  
  20.                 int forward[2];  
  21.                 if (pipe(forward)) {  
  22.                     ......  
  23.                 } else {  
  24.                     int reverse[2];  
  25.                     if (pipe(reverse)) {  
  26.                         ......  
  27.                     } else {  
  28.                         String8 serverChannelName = name;  
  29.                         serverChannelName.append(" (server)");  
  30.                         outServerChannel = new InputChannel(serverChannelName,  
  31.                             serverAshmemFd, reverse[0], forward[1]);  
  32.   
  33.                         String8 clientChannelName = name;  
  34.                         clientChannelName.append(" (client)");  
  35.                         outClientChannel = new InputChannel(clientChannelName,  
  36.                             clientAshmemFd, forward[0], reverse[1]);  
  37.                         return OK;  
  38.                     }  
  39.                     ......  
  40.                 }  
  41.                 ......  
  42.             }  
  43.         }  
  44.     }  
  45.     ......  
  46. }  

        在阅读这个函数之前,我们首先了解一下C++层的InputChannel的构造函数:

  1. InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,  
  2.     int32_t sendPipeFd) :  
  3.     mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {  
  4.     ......  
  5. }   

        为了创建一个InputChannel,我们需要准备四个参数,一个是输入通道的名称name,一个是匿名共享内存文件描述符,一个是管道的读端文件描述符,一个是管道的写端文件描述符。在上面的openInputChannelPair函数,输入通道的名称已经作为参数传递进来,因此,还需要创建匿名共享内存文件,还有管道。这里需要创建两个管道,一个称为前向管道(forward pipe),一个称为反向管道(reverse pipe),它们交叉使用在Server端和Client端的InputChannel中,这样就使入Server和Client可以互相通信了。

 

        具体来说,Server端和Client端的InputChannel分别是这样构成的:

        Server Input Channel:  ashmem - reverse(read) - forward(write)

        Client Input Channel:   ashmem - forward(read) - reverse(write)
        前面我们在Android应用程序消息处理机制(Looper、Handler)分析一文中学习Android应用程序的消息处理机制时知道,管道可以用作进程间通信,其中一个进程在管道的读端等待新的内空可读,另一个进程在管道的写端写入新的内容以唤醒在管道读端等待的进程,这样就实现了进程间通信。在我们这个情景中,Client端可以在前向管道(forward pipe)的读端睡眠等待新的内容可读,而Server端可以通过向前向管道(forward pipe)的写端写入新的内容来唤醒Client端,同样,把前向管道(forward pipe)换成反向管道(reverse pipe),也能实现Client端唤醒Server端。在后面我们分析InputDispatcher分发键盘消息时,会看到它们的用法。

        有了这些背景知识后,相信上面的openInputChannelPair的代码就容易理解了,这里就不再详述了。

        创建好了这两个输入通道后,回到Step 11中的WindowManagerService.addWindow函数中,一方面它把刚才创建的Client端的输入通道通过outInputChannel参数返回到应用程序中:

  1. inputChannels[1].transferToBinderOutParameter(outInputChannel);  

       另一方面,它还要把刚才创建的Server端的输入通道注册到InputManager中:

  1. mInputManager.registerInputChannel(win.mInputChannel);  

       Step 15. InputManager.registerInputChannel

 

       这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     /** 
  5.     * Registers an input channel so that it can be used as an input event target. 
  6.     * @param inputChannel The input channel to register. 
  7.     */  
  8.     public void registerInputChannel(InputChannel inputChannel) {  
  9.         if (inputChannel == null) {  
  10.             throw new IllegalArgumentException("inputChannel must not be null.");  
  11.         }  
  12.   
  13.         nativeRegisterInputChannel(inputChannel, false);  
  14.     }  
  15.   
  16.     ......  
  17. }  

         它通过调用本地方法nativeRegisterInputChannel来执行进一步的操作。

 

         Step 16. InputManager.nativeRegisterInputChannel

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

  1. static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj, jboolean monitor) {  
  3.     ......  
  4.   
  5.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  6.        inputChannelObj);  
  7.     ......  
  8.   
  9.     status_t status = gNativeInputManager->registerInputChannel(  
  10.        env, inputChannel, inputChannelObj, monitor);  
  11.       
  12.     ......  
  13. }  

        这里首先通过Java层的InputChannel对象获得C++层的InputChannel对象,它们之间的对应关系是在前面的Step 13中设置好的,接着调用NativeInputManager的registerInputChannel执行进一步的操作。

 

        Step 17. NativeInputManager.registerInputChannel

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

  1. status_t NativeInputManager::registerInputChannel(JNIEnv* env,  
  2.         const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {  
  3.     ......  
  4.   
  5.     status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);  
  6.       
  7.     ......  
  8. }  

        这个函数主要是调用了InputDispatcher的registerInputChannel来真正执行注册输入通道的操作。 

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