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

简介:

  Step 21. NativeInputQueue.handleReceiveCallback

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

  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.         ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  13.         ......  
  14.   
  15.             connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);  
  16.         ......  
  17.   
  18.   
  19.             status_t status = connection->inputConsumer.receiveDispatchSignal();  
  20.         if (status) {  
  21.             ......  
  22.                 return 0; // remove the callback  
  23.         }  
  24.   
  25.         ......  
  26.   
  27.         status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);  
  28.         ......  
  29.   
  30.         finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);  
  31.   
  32.         inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);  
  33.     } // release lock  
  34.   
  35.     ......  
  36.   
  37.     int32_t inputEventType = inputEvent->getType();  
  38.   
  39.     jobject inputEventObj;  
  40.     jmethodID dispatchMethodId;  
  41.     switch (inputEventType) {  
  42.     case AINPUT_EVENT_TYPE_KEY:  
  43.         ......  
  44.             inputEventObj = android_view_KeyEvent_fromNative(env,  
  45.             static_cast<KeyEvent*>(inputEvent));  
  46.         dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;  
  47.         break;  
  48.     }  
  49.     ......  
  50.     }  
  51.   
  52.     ......  
  53.   
  54.     env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,  
  55.                           dispatchMethodId, inputHandlerObjLocal, inputEventObj,  
  56.                           jlong(finishedToken));  
  57.   
  58.     ......  
  59.   
  60.     return 1;  
  61. }  

 

        这个函数首先是通过参数data获得当初注册InputChannel的NativeInputQueue对象,具体可以参考前面介绍的应用程序注册键盘消息接收通道过程的Step 21。接下来再通过参数receiveFd获得保存在这个NativeInputQueue对象中的mConnectionsByReceiveFd成员变量中的Connection对象。有了这个Connection对象后,就可以获得它内部的InputConsumer对象,这个InputConsumer对象是和上面的Step 18中介绍的InputPublisher对象相应的。

 

        在InputChannel内部中,分别有一个InputPublisher对象和一个InputConsumer对象,对于Server端的InputChannel来说,它使用的是InputPublisher对象,通过它进行键盘消息的分发,而对于Client端的InputChannel来说,它使用的是InputConsumer对象,通过它进行键盘消息的读取。

        获得了这个InputConsumer对象后首先是调用它的receiveDispatchSignal来确认是否是接收到了键盘消息的通知,如果是的话,再调用它的consume函数来把键盘事件读取出来,最后,调用Java层的回调对象InputQueue的DispatchKeyEvent来处理这个键盘事件。下面,我们就依次来分析这些过程。

        Step 22. InputConsumer.receiveDispatchSignal

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

  1. status_t InputConsumer::receiveDispatchSignal() {  
  2.     ......  
  3.   
  4.     char signal;  
  5.     status_t result = mChannel->receiveSignal(& signal);  
  6.     if (result) {  
  7.         return result;  
  8.     }  
  9.     if (signal != INPUT_SIGNAL_DISPATCH) {  
  10.         ......  
  11.         return UNKNOWN_ERROR;  
  12.     }  
  13.     return OK;  
  14. }  

         这个函数很简单,它通过它内部对象mChannel来从前向管道的读端读入一个字符,看看是否是前面的Step 20中写入的INPUT_SIGNAL_DISPATCH字符。

 

         InputChannel类的receiveSignal函数也是定义在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputChannel::receiveSignal(char* outSignal) {  
  2.     ssize_t nRead;  
  3.     do {  
  4.         nRead = ::read(mReceivePipeFd, outSignal, 1);  
  5.     } while (nRead == -1 && errno == EINTR);  
  6.   
  7.     if (nRead == 1) {  
  8.         ......  
  9.         return OK;  
  10.     }  
  11.   
  12.     ......  
  13.     return -errno;  
  14. }  

        Step 23. InputConsumer.consume

 

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

  1. status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {  
  2.     ......  
  3.   
  4.     *outEvent = NULL;  
  5.   
  6.     int ashmemFd = mChannel->getAshmemFd();  
  7.     int result = ashmem_pin_region(ashmemFd, 0, 0);  
  8.     ......  
  9.   
  10.     if (mSharedMessage->consumed) {  
  11.         ......  
  12.         return INVALID_OPERATION;  
  13.     }  
  14.   
  15.     // Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal  
  16.     // to the publisher that the message has been consumed (or is in the process of being  
  17.     // consumed).  Eventually the publisher will reinitialize the semaphore for the next message.  
  18.     result = sem_wait(& mSharedMessage->semaphore);  
  19.     ......  
  20.   
  21.     mSharedMessage->consumed = true;  
  22.   
  23.     switch (mSharedMessage->type) {  
  24.     case AINPUT_EVENT_TYPE_KEY: {  
  25.         KeyEvent* keyEvent = factory->createKeyEvent();  
  26.         if (! keyEvent) return NO_MEMORY;  
  27.   
  28.         populateKeyEvent(keyEvent);  
  29.   
  30.         *outEvent = keyEvent;  
  31.         break;  
  32.     }  
  33.     ......  
  34.     }  
  35.   
  36.     return OK;  
  37. }  

        这个函数很简单,只要对照前面的Step 18(InputPublisher.publishKeyEvent)来逻辑来看就可以了,后者是往匿名共享内存中写入键盘事件,前者是从这个匿名共享内存中把这个键盘事件的内容读取出来。

        回到Step 21中的handleReceiveCallback函数中,从InputConsumer中获得了键盘事件的内容(保存在本地变量inputEvent中)后,就开始要通知Java层的应用程序了。在前面分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),会把传进来的对象inputHandlerObj保存在Connection对象中:

  1. connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);  

        这个inputHandlerObj对象的类型为Java层的InputHandler对象,因此,这里首先把它取回来:

 

  1. inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);  

        取回来之后,我们要把作为参数来调用InputQueue类的dispatchKeyEvent静态成员函数来通知应用程序,有键盘事件发生了,因此,先找到InputQueue类的静态成员函数dispatchKeyEvent的ID:

  1. dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;  

        在回调用这个InputQueue类的dispatchKeyEvent静态成员函数之前,还要把前面获得的inputEvent对象转换成Java层的KeyEvent对象:

  1. inputEventObj = android_view_KeyEvent_fromNative(env,  
  2.     static_cast<KeyEvent*>(inputEvent));  

        万事具备了,就可以通知Java层的InputQueue来处理这个键盘事件了:

  1. env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,  
  2.         dispatchMethodId, inputHandlerObjLocal, inputEventObj,  
  3.         jlong(finishedToken));  




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966638,如需转载请自行联系原作者
目录
相关文章
|
5月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
625 4
|
5月前
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
5月前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
227 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
2月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
74 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
4月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
145 14
|
5月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
212 21
|
4月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
101 13
|
4月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
93 8
|
4月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
下一篇
oss创建bucket