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