android 键值老罗,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(data);

JNIEnv* env = AndroidRuntime::getJNIEnv();

sp connection;

InputEvent* inputEvent;

jobject inputHandlerObjLocal;

jlong finishedToken;

{ // acquire lock

AutoMutex _l(q->mLock);

ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);

......

connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);

......

status_t status = connection->inputConsumer.receiveDispatchSignal();

if(status) {

......

return0;// remove the callback

}

......

status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);

......

finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);

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

} // release lock

......

int32_t inputEventType = inputEvent->getType();

jobject inputEventObj;

jmethodID dispatchMethodId;

switch(inputEventType) {

caseAINPUT_EVENT_TYPE_KEY:

......

inputEventObj = android_view_KeyEvent_fromNative(env,

static_cast(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_t InputConsumer::receiveDispatchSignal() {

......

charsignal;

status_t result = 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_t InputChannel::receiveSignal(char* outSignal) {

ssize_t nRead;

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_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {

......

*outEvent = NULL;

intashmemFd = mChannel->getAshmemFd();

intresult = ashmem_pin_region(ashmemFd, 0, 0);

......

if(mSharedMessage->consumed) {

......

returnINVALID_OPERATION;

}

// Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal

// to the publisher that the message has been consumed (or is in the process of being

// consumed).  Eventually the publisher will reinitialize the semaphore for the next message.

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(inputEvent));

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

env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,

dispatchMethodId, inputHandlerObjLocal, inputEventObj,

jlong(finishedToken));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值