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

  1. status_tInputDispatcher::registerInputChannel(constsp<InputChannel>&inputChannel,boolmonitor){
  2. ......
  3. {//acquirelock
  4. AutoMutex_l(mLock);
  5. if(getConnectionIndexLocked(inputChannel)>=0){
  6. LOGW("Attemptedtoregisteralreadyregisteredinputchannel'%s'",
  7. inputChannel->getName().string());
  8. returnBAD_VALUE;
  9. }
  10. sp<Connection>connection=newConnection(inputChannel);
  11. status_tstatus=connection->initialize();
  12. if(status){
  13. LOGE("Failedtoinitializeinputpublisherforinputchannel'%s',status=%d",
  14. inputChannel->getName().string(),status);
  15. returnstatus;
  16. }
  17. int32_treceiveFd=inputChannel->getReceivePipeFd();
  18. mConnectionsByReceiveFd.add(receiveFd,connection);
  19. if(monitor){
  20. mMonitoringChannels.push(inputChannel);
  21. }
  22. mLooper->addFd(receiveFd,0,ALOOPER_EVENT_INPUT,handleReceiveCallback,this);
  23. runCommandsLockedInterruptible();
  24. }//releaselock
  25. returnOK;
  26. }

这个函数首先会通过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(viewinstanceofRootViewSurfaceTaker){
  2. mInputQueueCallback=
  3. ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
  4. }
  5. if(mInputQueueCallback!=null){
  6. mInputQueue=newInputQueue(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. publicfinalclassInputQueue{
  2. ......
  3. publicstaticvoidregisterInputChannel(InputChannelinputChannel,InputHandlerinputHandler,
  4. MessageQueuemessageQueue){
  5. ......
  6. synchronized(sLock){
  7. ......
  8. nativeRegisterInputChannel(inputChannel,inputHandler,messageQueue);
  9. }
  10. }
  11. ......
  12. }

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

Step 20.InputQueue.nativeRegisterInputChannel

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

  1. staticvoidandroid_view_InputQueue_nativeRegisterInputChannel(JNIEnv*env,jclassclazz,
  2. jobjectinputChannelObj,jobjectinputHandlerObj,jobjectmessageQueueObj){
  3. status_tstatus=gNativeInputQueue.registerInputChannel(
  4. env,inputChannelObj,inputHandlerObj,messageQueueObj);
  5. ......
  6. }

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

Step 21. NativeInputQueue.registerInputChannel

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

  1. status_tNativeInputQueue::registerInputChannel(JNIEnv*env,jobjectinputChannelObj,
  2. jobjectinputHandlerObj,jobjectmessageQueueObj){
  3. sp<InputChannel>inputChannel=android_view_InputChannel_getInputChannel(env,
  4. inputChannelObj);
  5. ......
  6. sp<Looper>looper=android_os_MessageQueue_getLooper(env,messageQueueObj);
  7. {//acquirelock
  8. AutoMutex_l(mLock);
  9. if(getConnectionIndex(inputChannel)>=0){
  10. LOGW("Attemptedtoregisteralreadyregisteredinputchannel'%s'",
  11. inputChannel->getName().string());
  12. returnBAD_VALUE;
  13. }
  14. uint16_tconnectionId=mNextConnectionId++;
  15. sp<Connection>connection=newConnection(connectionId,inputChannel,looper);
  16. status_tresult=connection->inputConsumer.initialize();
  17. if(result){
  18. LOGW("Failedtoinitializeinputconsumerforinputchannel'%s',status=%d",
  19. inputChannel->getName().string(),result);
  20. returnresult;
  21. }
  22. connection->inputHandlerObjGlobal=env->NewGlobalRef(inputHandlerObj);
  23. int32_treceiveFd=inputChannel->getReceivePipeFd();
  24. mConnectionsByReceiveFd.add(receiveFd,connection);
  25. looper->addFd(receiveFd,0,ALOOPER_EVENT_INPUT,handleReceiveCallback,this);
  26. }//releaselock
  27. ......
  28. returnOK;
  29. }

这里注册应用程序的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分发键盘消息给应用程序的过程了。

更多相关文章

  1. android的webview调用javascript函数并得到返回值
  2. Android 数据查询query函数参数解析
  3. android 应用程序退出
  4. Android应用程序键盘(Keyboard)消息处理机制分析(3)

随机推荐

  1. Android Battery 架构
  2. android全屏去掉title栏的多种实现方法
  3. android地图开发的主要提供商。
  4. android中如何获取文件的路径总结
  5. 为什么Android不是GPL许可证?
  6. Android触摸屏幕时间-android学习之旅(三)
  7. ActionbarSherlock的Style讲解
  8. Android slidingmenu详细解释 滑动的优化
  9. 在Android中通过jni方式使用编译好的FFmp
  10. Android应用