Android应用程序键盘(Keyboard)消息处理机制分析(19)
Step 21.NativeInputQueue.handleReceiveCallback
这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- intNativeInputQueue::handleReceiveCallback(intreceiveFd,intevents,void*data){
- NativeInputQueue*q=static_cast<NativeInputQueue*>(data);
- JNIEnv*env=AndroidRuntime::getJNIEnv();
- sp<Connection>connection;
- InputEvent*inputEvent;
- jobjectinputHandlerObjLocal;
- jlongfinishedToken;
- {//acquirelock
- AutoMutex_l(q->mLock);
- ssize_tconnectionIndex=q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- connection=q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- ......
- status_tstatus=connection->inputConsumer.receiveDispatchSignal();
- if(status){
- ......
- return0;//removethecallback
- }
- ......
- status=connection->inputConsumer.consume(&connection->inputEventFactory,&inputEvent);
- ......
- finishedToken=generateFinishedToken(receiveFd,connection->id,connection->messageSeqNum);
- inputHandlerObjLocal=env->NewLocalRef(connection->inputHandlerObjGlobal);
- }//releaselock
- ......
- int32_tinputEventType=inputEvent->getType();
- jobjectinputEventObj;
- jmethodIDdispatchMethodId;
- switch(inputEventType){
- caseAINPUT_EVENT_TYPE_KEY:
- ......
- inputEventObj=android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- dispatchMethodId=gInputQueueClassInfo.dispatchKeyEvent;
- break;
- }
- ......
- }
- ......
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId,inputHandlerObjLocal,inputEventObj,
- jlong(finishedToken));
- ......
- return1;
- }
这个函数首先是通过参数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文件中:
- status_tInputConsumer::receiveDispatchSignal(){
- ......
- charsignal;
- status_tresult=mChannel->receiveSignal(&signal);
- if(result){
- returnresult;
- }
- if(signal!=INPUT_SIGNAL_DISPATCH){
- ......
- returnUNKNOWN_ERROR;
- }
- returnOK;
- }
这个函数很简单,它通过它内部对象mChannel来从前向管道的读端读入一个字符,看看是否是前面的Step 20中写入的INPUT_SIGNAL_DISPATCH字符。
InputChannel类的receiveSignal函数也是定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_tInputChannel::receiveSignal(char*outSignal){
- ssize_tnRead;
- do{
- nRead=::read(mReceivePipeFd,outSignal,1);
- }while(nRead==-1&&errno==EINTR);
- if(nRead==1){
- ......
- returnOK;
- }
- ......
- return-errno;
- }
Step 23.InputConsumer.consume
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_tInputConsumer::consume(InputEventFactoryInterface*factory,InputEvent**outEvent){
- ......
- *outEvent=NULL;
- intashmemFd=mChannel->getAshmemFd();
- intresult=ashmem_pin_region(ashmemFd,0,0);
- ......
- if(mSharedMessage->consumed){
- ......
- returnINVALID_OPERATION;
- }
- //Acquirebut*neverrelease*thesemaphore.Contentiononthesemaphoreisusedtosignal
- //tothepublisherthatthemessagehasbeenconsumed(orisintheprocessofbeing
- //consumed).Eventuallythepublisherwillreinitializethesemaphoreforthenextmessage.
- result=sem_wait(&mSharedMessage->semaphore);
- ......
- mSharedMessage->consumed=true;
- switch(mSharedMessage->type){
- caseAINPUT_EVENT_TYPE_KEY:{
- KeyEvent*keyEvent=factory->createKeyEvent();
- if(!keyEvent)returnNO_MEMORY;
- populateKeyEvent(keyEvent);
- *outEvent=keyEvent;
- break;
- }
- ......
- }
- returnOK;
- }
这个函数很简单,只要对照前面的Step 18(InputPublisher.publishKeyEvent)来逻辑来看就可以了,后者是往匿名共享内存中写入键盘事件,前者是从这个匿名共享内存中把这个键盘事件的内容读取出来。
回到Step 21中的handleReceiveCallback函数中,从InputConsumer中获得了键盘事件的内容(保存在本地变量inputEvent中)后,就开始要通知Java层的应用程序了。在前面分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),会把传进来的对象inputHandlerObj保存在Connection对象中:
- connection->inputHandlerObjGlobal=env->NewGlobalRef(inputHandlerObj);
这个inputHandlerObj对象的类型为Java层的InputHandler对象,因此,这里首先把它取回来:
- inputHandlerObjLocal=env->NewLocalRef(connection->inputHandlerObjGlobal);
取回来之后,我们要把作为参数来调用InputQueue类的dispatchKeyEvent静态成员函数来通知应用程序,有键盘事件发生了,因此,先找到InputQueue类的静态成员函数dispatchKeyEvent的ID:
- dispatchMethodId=gInputQueueClassInfo.dispatchKeyEvent;
在回调用这个InputQueue类的dispatchKeyEvent静态成员函数之前,还要把前面获得的inputEvent对象转换成Java层的KeyEvent对象:
- inputEventObj=android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
万事具备了,就可以通知Java层的InputQueue来处理这个键盘事件了:
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId,inputHandlerObjLocal,inputEventObj,
- jlong(finishedToken));
更多相关文章
- Android 软键盘的显示和隐藏
- 利用BeautifulSoup的find_all()函数查找某个标签且该标签某属性
- 自定义android用户控件,使用回调函数实现自定义事件
- 在Android里添加自己的log函数
- android 软键盘Enter键事件处理
- Android 如何获取当前Activity实例对象?
- android中监听软键盘的弹出与隐藏,并获取软键盘的高度
- Android触发事件总结(触摸屏事件,手势识别,键盘事件,模拟鼠标/按键事