Android应用程序键盘(Keyboard)消息处理机制分析(三)
3.InputManager分发键盘消息给应用程序的过程分析
在分析InputManager分发键盘消息给应用程序的过程之前,我们先假设现在没有键盘事件发生,因此,InputManager中的InputReader正在睡眠等待键盘事件的发生,而InputManager中的InputDispatcher正在等待InputReader从睡眠中醒过来并且唤醒它,而应用程序也正在消息循环中等待InputDispatcher从睡眠中醒过来并且唤醒它。这时候,用户按下键盘中的一个键,于是,一系列唤醒的事件就依次发生了,一直到应用程序中正在显示的Activity得到通知,有键盘事件发生了。我们先来看这个过程的序列图,然后再详细分析每一个步骤:
点击查看大图
Step 1. InputReader.pollOnce
Step 2. EventHub.getEvent
这两个函数分别定义在frameworks/base/libs/ui/InputReader.cpp和frameworks/base/libs/ui/EventHub.cpp文件中,前面我们在分析InputManager的启动过程的Step 17和Step 18时,已经看到过这两个函数了。InputReaderThread线程会不民地循环调用InputReader.pollOnce函数来读入键盘事件,而实际的键盘事件读入操作是由EventHub.getEvent函数来进行的。如果当前没有键盘事件发生,InputReaderThread线程就会睡眠在EventHub.getEvent函数上,而当键盘事件发生后,就会把这个事件封装成一个RawEvent对象,然后返回到pollOnce函数中,执行process函数进一步处理:
view plain
- voidInputReader::loopOnce(){
- RawEventrawEvent;
- mEventHub->getEvent(&rawEvent);
- ......
- process(&rawEvent);
- }
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
view plain
- voidInputReader::process(constRawEvent*rawEvent){
- switch(rawEvent->type){
- caseEventHubInterface::DEVICE_ADDED:
- addDevice(rawEvent->deviceId);
- break;
- caseEventHubInterface::DEVICE_REMOVED:
- removeDevice(rawEvent->deviceId);
- break;
- caseEventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChanged(rawEvent->when);
- break;
- default:
- consumeEvent(rawEvent);
- break;
- }
- }
当键盘事件发生时,rawEvent->type的值为EV_KEY,这是一个宏定义,具体可以参考bionic/libc/kernel/common/linux/input.h文件:
view plain
- #defineEV_KEY0x01
Step 4.InputReader.consumeEvent
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
view plain
- voidInputReader::consumeEvent(constRawEvent*rawEvent){
- int32_tdeviceId=rawEvent->deviceId;
- {//acquiredeviceregistryreaderlock
- RWLock::AutoRLock_rl(mDeviceRegistryLock);
- ssize_tdeviceIndex=mDevices.indexOfKey(deviceId);
- if(deviceIndex<0){
- LOGW("DiscardingeventforunknowndeviceId%d.",deviceId);
- return;
- }
- InputDevice*device=mDevices.valueAt(deviceIndex);
- if(device->isIgnored()){
- //LOGD("DiscardingeventforignoreddeviceId%d.",deviceId);
- return;
- }
- device->process(rawEvent);
- }//releasedeviceregistryreaderlock
- }
Step 5. InputDevice.process
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
view plain
- voidInputDevice::process(constRawEvent*rawEvent){
- size_tnumMappers=mMappers.size();
- for(size_ti=0;i<numMappers;i++){
- InputMapper*mapper=mMappers[i];
- mapper->process(rawEvent);
- }
- }
Step 6.KeyboardInputMapper.process
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
view plain
- voidKeyboardInputMapper::process(constRawEvent*rawEvent){
- switch(rawEvent->type){
- caseEV_KEY:{
- int32_tscanCode=rawEvent->scanCode;
- if(isKeyboardOrGamepadKey(scanCode)){
- processKey(rawEvent->when,rawEvent->value!=0,rawEvent->keyCode,scanCode,
- rawEvent->flags);
- }
- break;
- }
- }
- }
Step 7.KeyboardInputMapper.processKey
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
- voidKeyboardInputMapper::processKey(nsecs_twhen,booldown,int32_tkeyCode,
- int32_tscanCode,uint32_tpolicyFlags){
- int32_tnewMetaState;
- nsecs_tdownTime;
- boolmetaStateChanged=false;
- {//acquirelock
- AutoMutex_l(mLock);
- if(down){
- //Rotatekeycodesaccordingtoorientationifneeded.
- //Note:getDisplayInfoisnon-reentrantsowecancontinueholdingthelock.
- if(mAssociatedDisplayId>=0){
- int32_torientation;
- if(!getPolicy()->getDisplayInfo(mAssociatedDisplayId,NULL,NULL,&orientation)){
- return;
- }
- keyCode=rotateKeyCode(keyCode,orientation);
- }
- //Addkeydown.
- ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
- if(keyDownIndex>=0){
- //keyrepeat,besuretousesamekeycodeasbeforeincaseofrotation
- keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- }else{
- //keydown
- if((policyFlags&POLICY_FLAG_VIRTUAL)
- &&mContext->shouldDropVirtualKey(when,getDevice(),keyCode,scanCode)){
- return;
- }
- mLocked.keyDowns.push();
- KeyDown&keyDown=mLocked.keyDowns.editTop();
- keyDown.keyCode=keyCode;
- keyDown.scanCode=scanCode;
- }
- mLocked.downTime=when;
- }else{
- //Removekeydown.
- ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
- if(keyDownIndex>=0){
- //keyup,besuretousesamekeycodeasbeforeincaseofrotation
- keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- mLocked.keyDowns.removeAt(size_t(keyDownIndex));
- }else{
- //keywasnotactuallydown
- LOGI("Droppingkeyupfromdevice%sbecausethekeywasnotdown."
- "keyCode=%d,scanCode=%d",
- getDeviceName().string(),keyCode,scanCode);
- return;
- }
- }
- int32_toldMetaState=mLocked.metaState;
- newMetaState=updateMetaState(keyCode,down,oldMetaState);
- if(oldMetaState!=newMetaState){
- mLocked.metaState=newMetaState;
- metaStateChanged=true;
- }
- downTime=mLocked.downTime;
- }//releaselock
- if(metaStateChanged){
- getContext()->updateGlobalMetaState();
- }
- getDispatcher()->notifyKey(when,getDeviceId(),AINPUT_SOURCE_KEYBOARD,policyFlags,
- down?AKEY_EVENT_ACTION_DOWN:AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM,keyCode,scanCode,newMetaState,downTime);
- }
view plain
- //Rotatekeycodesaccordingtoorientationifneeded.
- //Note:getDisplayInfoisnon-reentrantsowecancontinueholdingthelock.
- if(mAssociatedDisplayId>=0){
- int32_torientation;
- if(!getPolicy()->getDisplayInfo(mAssociatedDisplayId,NULL,NULL,&orientation)){
- return;
- }
- keyCode=rotateKeyCode(keyCode,orientation);
- }
view plain
- //Addkeydown.
- ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
- if(keyDownIndex>=0){
- //keyrepeat,besuretousesamekeycodeasbeforeincaseofrotation
- keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- }else{
- //keydown
- if((policyFlags&POLICY_FLAG_VIRTUAL)
- &&mContext->shouldDropVirtualKey(when,getDevice(),keyCode,scanCode)){
- return;
- }
- mLocked.keyDowns.push();
- KeyDown&keyDown=mLocked.keyDowns.editTop();
- keyDown.keyCode=keyCode;
- keyDown.scanCode=scanCode;
- }
如果是松开键盘上的某个键,就把它从mLocked.keyDowns里面删除:
view plain
- //Removekeydown.
- ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
- if(keyDownIndex>=0){
- //keyup,besuretousesamekeycodeasbeforeincaseofrotation
- keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- mLocked.keyDowns.removeAt(size_t(keyDownIndex));
- }else{
- //keywasnotactuallydown
- LOGI("Droppingkeyupfromdevice%sbecausethekeywasnotdown."
- "keyCode=%d,scanCode=%d",
- getDeviceName().string(),keyCode,scanCode);
- return;
- }
最后,KeyboardInputMappger函数通知InputDispatcher,有键盘事件发生了:
view plain
- getDispatcher()->notifyKey(when,getDeviceId(),AINPUT_SOURCE_KEYBOARD,policyFlags,
- down?AKEY_EVENT_ACTION_DOWN:AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM,keyCode,scanCode,newMetaState,downTime);
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- voidInputDispatcher::notifyKey(nsecs_teventTime,int32_tdeviceId,int32_tsource,
- uint32_tpolicyFlags,int32_taction,int32_tflags,
- int32_tkeyCode,int32_tscanCode,int32_tmetaState,nsecs_tdownTime){
- ......
- if(!validateKeyEvent(action)){
- return;
- }
- /*Accordingtohttp://source.android.com/porting/keymaps_keyboard_input.html
- *Keydefinitions:KeydefinitionsfollowthesyntaxkeySCANCODEKEYCODE[FLAGS...],
- *whereSCANCODEisanumber,KEYCODEisdefinedinyourspecifickeylayoutfile
- *(android.keylayout.xxx),andpotentialFLAGSaredefinedasfollows:
- *SHIFT:Whilepressed,theshiftkeymodifierisset
- *ALT:Whilepressed,thealtkeymodifierisset
- *CAPS:Whilepressed,thecapslockkeymodifierisset
- *SinceKeyEvent.javadoesn'tcheckifCaplockisONandwedon'thavea
- *modiferstateforcaplock,wewillnotsupportit.
- */
- if(policyFlags&POLICY_FLAG_ALT){
- metaState|=AMETA_ALT_ON|AMETA_ALT_LEFT_ON;
- }
- if(policyFlags&POLICY_FLAG_ALT_GR){
- metaState|=AMETA_ALT_ON|AMETA_ALT_RIGHT_ON;
- }
- if(policyFlags&POLICY_FLAG_SHIFT){
- metaState|=AMETA_SHIFT_ON|AMETA_SHIFT_LEFT_ON;
- }
- policyFlags|=POLICY_FLAG_TRUSTED;
- mPolicy->interceptKeyBeforeQueueing(eventTime,deviceId,action,/*byref*/flags,
- keyCode,scanCode,/*byref*/policyFlags);
- boolneedWake;
- {//acquirelock
- AutoMutex_l(mLock);
- int32_trepeatCount=0;
- KeyEntry*newEntry=mAllocator.obtainKeyEntry(eventTime,
- deviceId,source,policyFlags,action,flags,keyCode,scanCode,
- metaState,repeatCount,downTime);
- needWake=enqueueInboundEventLocked(newEntry);
- }//releaselock
- if(needWake){
- mLooper->wake();
- }
- }
view plain
- staticboolisValidKeyAction(int32_taction){
- switch(action){
- caseAKEY_EVENT_ACTION_DOWN:
- caseAKEY_EVENT_ACTION_UP:
- returntrue;
- default:
- returnfalse;
- }
- }
- staticboolvalidateKeyEvent(int32_taction){
- if(!isValidKeyAction(action)){
- LOGE("Keyeventhasinvalidactioncode0x%x",action);
- returnfalse;
- }
- returntrue;
- }
参数action检查通过后,还通过policyFlags参数来检查一下同时是否有ALT和SHIFT键被按下:
view plain
- if(policyFlags&POLICY_FLAG_ALT){
- metaState|=AMETA_ALT_ON|AMETA_ALT_LEFT_ON;
- }
- if(policyFlags&POLICY_FLAG_ALT_GR){
- metaState|=AMETA_ALT_ON|AMETA_ALT_RIGHT_ON;
- }
- if(policyFlags&POLICY_FLAG_SHIFT){
- metaState|=AMETA_SHIFT_ON|AMETA_SHIFT_LEFT_ON;
- }
view plain
- boolInputDispatcher::enqueueInboundEventLocked(EventEntry*entry){
- boolneedWake=mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(entry);
- switch(entry->type){
- caseEventEntry::TYPE_KEY:{
- KeyEntry*keyEntry=static_cast<KeyEntry*>(entry);
- if(isAppSwitchKeyEventLocked(keyEntry)){
- if(keyEntry->action==AKEY_EVENT_ACTION_DOWN){
- mAppSwitchSawKeyDown=true;
- }elseif(keyEntry->action==AKEY_EVENT_ACTION_UP){
- if(mAppSwitchSawKeyDown){
- <spanstyle="white-space:pre"></span>......
- mAppSwitchDueTime=keyEntry->eventTime+APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown=false;
- needWake=true;
- }
- }
- }
- break;
- }
- }
- returnneedWake;
- }
回到前面的notifyKey函数中,根据enqueueInboundEventLocked函数的返回值来决定是否要唤醒InputDispatccherThread线程:
view plain
- if(needWake){
- mLooper->wake();
- }
Step 9. Looper.wake
这个函数定义在frameworks/base/libs/utils/Looper.cpp文件中,在前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析中,我们已经分析过这个函数了,这里不再详述,简单来说,它的作用就是用来唤醒睡眠在Looper对象内部的管道读端的线程,在我们的这个场景中,睡眠在Looper对象内部的管道读端的线程就是InputDispatccherThread线程了。
从上面InputManager启动过程的Step 15中,我们知道,此时InputDispatccherThread线程正在InputDispatcher类的dispatchOnceb函数中通过调用mLooper->loopOnce函数进入睡眠状态。当它被唤醒以后,它就会从InputDispatcher类的dispatchOnceb函数返回到InputDispatcherThread类的threadLoop函数,而InputDispatcherThread类的threadLoop函数是循环执行的,于是,它又会再次进入到InputDispatcher类的dispatchOnce函数来处理当前发生的键盘事件。
Step 10.InputDispatcher.dispatchOnce
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- voidInputDispatcher::dispatchOnce(){
- nsecs_tkeyRepeatTimeout=mPolicy->getKeyRepeatTimeout();
- nsecs_tkeyRepeatDelay=mPolicy->getKeyRepeatDelay();
- nsecs_tnextWakeupTime=LONG_LONG_MAX;
- {//acquirelock
- AutoMutex_l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout,keyRepeatDelay,&nextWakeupTime);
- ......
- }//releaselock
- ......
- }
Step 11.InputDispatcher.dispatchOnceInnerLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain- voidInputDispatcher::dispatchOnceInnerLocked(nsecs_tkeyRepeatTimeout,
- nsecs_tkeyRepeatDelay,nsecs_t*nextWakeupTime){
- ......
- //Readytostartanewevent.
- //Ifwedon'talreadyhaveapendingevent,gograbone.
- if(!mPendingEvent){
- if(mInboundQueue.isEmpty()){
- ......
- }else{
- //Inboundqueuehasatleastoneentry.
- EventEntry*entry=mInboundQueue.headSentinel.next;
- ......
- mInboundQueue.dequeue(entry);
- mPendingEvent=entry;
- }
- ......
- }
- ......
- switch(mPendingEvent->type){
- ......
- caseEventEntry::TYPE_KEY:{
- KeyEntry*typedEntry=static_cast<KeyEntry*>(mPendingEvent);
- ......
- done=dispatchKeyLocked(currentTime,typedEntry,keyRepeatTimeout,
- &dropReason,nextWakeupTime);
- break;
- }
- ......
- }
- ......
- }
我们忽略了这个函数的次要逻辑,主要关注键盘事件的主要处理流程。首先,如果前面发生的键盘事件都已经处理完毕,那么这里的mPendingEvent就为NULL,又因为前面我们把刚刚发生的键盘事件加入了mInboundQueue队列,因此,这里mInboundQueue不为NULL,于是,这里就把mInboundQueue队列中的键盘事件取出来,放在mPendingEvent变量中:
view plain
- mInboundQueue.dequeue(entry);
- mPendingEvent=entry;
Step 12.InputDispatcher.dispatchKeyLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- boolInputDispatcher::dispatchKeyLocked(
- nsecs_tcurrentTime,KeyEntry*entry,nsecs_tkeyRepeatTimeout,
- DropReason*dropReason,nsecs_t*nextWakeupTime){
- ......
- //Identifytargets.
- if(!mCurrentInputTargetsValid){
- int32_tinjectionResult=findFocusedWindowTargetsLocked(currentTime,
- entry,nextWakeupTime);
- ......
- }
- //Dispatchthekey.
- dispatchEventToCurrentInputTargetsLocked(currentTime,entry,false);
- returntrue;
- }
我们先来看一InputDispatcher是如何找到当前激活的Activity窗口的,然后再分析它把键盘事件分发给当前激活Activity窗口的过程。
Step 13.InputDispatcher.findFocusedWindowTargetsLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- int32_tInputDispatcher::findFocusedWindowTargetsLocked(nsecs_tcurrentTime,
- constEventEntry*entry,nsecs_t*nextWakeupTime){
- mCurrentInputTargets.clear();
- int32_tinjectionResult;
- //Ifthereisnocurrentlyfocusedwindowandnofocusedapplication
- //thendroptheevent.
- if(!mFocusedWindow){
- if(mFocusedApplication){
- ......
- injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
- mFocusedApplication,NULL,nextWakeupTime);
- gotoUnresponsive;
- }
- ......
- injectionResult=INPUT_EVENT_INJECTION_FAILED;
- gotoFailed;
- }
- //Checkpermissions.
- if(!checkInjectionPermission(mFocusedWindow,entry->injectionState)){
- injectionResult=INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- gotoFailed;
- }
- //Ifthecurrentlyfocusedwindowispausedthenkeepwaiting.
- if(mFocusedWindow->paused){
- ......
- injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
- mFocusedApplication,mFocusedWindow,nextWakeupTime);
- gotoUnresponsive;
- }
- //Ifthecurrentlyfocusedwindowisstillworkingonpreviouseventsthenkeepwaiting.
- if(!isWindowFinishedWithPreviousInputLocked(mFocusedWindow)){
- ......
- injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
- mFocusedApplication,mFocusedWindow,nextWakeupTime);
- gotoUnresponsive;
- }
- //Success!Outputtargets.
- injectionResult=INPUT_EVENT_INJECTION_SUCCEEDED;
- addWindowTargetLocked(mFocusedWindow,InputTarget::FLAG_FOREGROUND,BitSet32(0));
- ......
- returninjectionResult;
- }
第二个if语句检查权限问题,原来,这个键盘事件除了是由硬件触发的外,也可以由其它进程注入进来的,如果这个键盘事件是由其它进程注入进来的,那么entry->injectState就不为NULL,它里面包含了事件注册者的进程ID和用户ID,于是,这里就会调用checkInjectionPermission来检查这个事件注入者的进程ID和用户ID,看看它是否具有这个权限。这里我们不考虑这种情况,因此,这里的entry->injectState为NULL,于是,这个if语句的检查也通过了。
第三个if语句检查当前激活的Activity窗口是否是处于paused状态,如果是的话,也不用进一步处理了。一般情况下,当前激活的Activity窗口都是处于resumed状态的,于是,这个if语句的检查也通过了。
第四个if语句检查当前激活的Activity窗口是否还正在处理前一个键盘事件,如果是的话,那就要等待它处理完前一个键盘事件后再来处理新的键盘事件了。这里我们也假设当前激活的Activity窗口不是正在处理前面的键盘事件,因此,这个if语句的检查也通过了。
最后,就调用addWindowTargetLocked函数把当前激活的Activity窗口添加到InputDispatcher类的mCurrentInputTargets成员变量中去。
Step 14.InputDispatcher.addWindowTargetLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain- voidInputDispatcher::addWindowTargetLocked(constInputWindow*window,int32_ttargetFlags,
- BitSet32pointerIds){
- mCurrentInputTargets.push();
- InputTarget&target=mCurrentInputTargets.editTop();
- target.inputChannel=window->inputChannel;
- target.flags=targetFlags;
- target.xOffset=-window->frameLeft;
- target.yOffset=-window->frameTop;
- target.pointerIds=pointerIds;
- }
回到Step 12中的dispatchKeyLocked函数,它接下来就调用dispatchEventToCurrentInputTargetsLocked来进一步处理了。
Step 15.InputDispatcher.dispatchEventToCurrentInputTargetsLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- voidInputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_tcurrentTime,
- EventEntry*eventEntry,boolresumeWithAppendedMotionSample){
- ......
- for(size_ti=0;i<mCurrentInputTargets.size();i++){
- constInputTarget&inputTarget=mCurrentInputTargets.itemAt(i);
- ssize_tconnectionIndex=getConnectionIndexLocked(inputTarget.inputChannel);
- if(connectionIndex>=0){
- sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex);
- prepareDispatchCycleLocked(currentTime,connection,eventEntry,&inputTarget,
- resumeWithAppendedMotionSample);
- }else{
- ......
- }
- }
前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封装成了一个Connection,然后以这个InputChannel中的Receive Pipe Fd作为键值把这个Connection对象保存在mConnectionsByReceiveFd中。这里,既然我们已经通过mCurrentInputTargets得到了表示当前需要接收键盘事件的Activity窗口的InputTarget对象,而且这个InputTarget对象的inputChannel就表示当初在InputDispatcher中注册的Server端InputChannel,因此,这里就可以把这个Connection对象取出来,最后调用prepareDispatchCycleLocked函数来进一步处理。
Step 16.InputDispatcher.prepareDispatchCycleLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain
- voidInputDispatcher::prepareDispatchCycleLocked(nsecs_tcurrentTime,
- constsp<Connection>&connection,EventEntry*eventEntry,constInputTarget*inputTarget,
- boolresumeWithAppendedMotionSample){
- ......
- //Resumethedispatchcyclewithafreshlyappendedmotionsample.
- //Firstwecheckthatthelastdispatchentryintheoutboundqueueisforthesame
- //motioneventtowhichweappendedthemotionsample.Ifwefindsuchadispatch
- //entry,andifitiscurrentlyinprogressthenwetrytostreamthenewsample.
- boolwasEmpty=connection->outboundQueue.isEmpty();
- if(!wasEmpty&&resumeWithAppendedMotionSample){
- ......
- return;
- }
- //Thisisanewevent.
- //Enqueueanewdispatchentryontotheoutboundqueueforthisconnection.
- DispatchEntry*dispatchEntry=mAllocator.obtainDispatchEntry(eventEntry,//incrementsref
- inputTarget->flags,inputTarget->xOffset,inputTarget->yOffset);
- ......
- //Enqueuethedispatchentry.
- connection->outboundQueue.enqueueAtTail(dispatchEntry);
- //Iftheoutboundqueuewaspreviouslyempty,startthedispatchcyclegoing.
- if(wasEmpty){
- ......
- startDispatchCycleLocked(currentTime,connection);
- }
- }
在开始处理键盘事件之前,这个函数会检查一下传进来的参数connection中的outboundQueue事件队列是否为空,如果不为空,就要看看当前要处理的事件和outboundQueue队列中的最后一个事件是不是同一个motion事件,如果是的话,并且从上面传进来的resumeWithAppendedMotionSample参数为true,这时候就要以流水线的方式来处理这些motion事件了。在我们这个情景中,要处理的是键盘事件,因此在上面Step 12中传进来的resumeWithAppendedMotionSample参数为false,因此,我们略过这种情况。
接下来,就会把当前的键盘事件封装成一个DispatchEntry对象,然后添加到connection对象的outboundQueue队列中去,表示当前键盘事件是一个待处理的键盘事件。
当connection中的outboundQueue事件队列不为空,即wasEmpty为false时,说明当前这个Activity窗口正在处键盘事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处键盘事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行键盘事件处理的流程了。
Step 17.InputDispatcher.startDispatchCycleLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
view plain- voidInputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime,
- constsp<Connection>&connection){
- ......
- DispatchEntry*dispatchEntry=connection->outboundQueue.headSentinel.next;
- //Markthedispatchentryasinprogress.
- dispatchEntry->inProgress=true;
- //Updatetheconnection'sinputstate.
- EventEntry*eventEntry=dispatchEntry->eventEntry;
- ......
- //Publishtheevent.
- status_tstatus;
- switch(eventEntry->type){
- caseEventEntry::TYPE_KEY:{
- KeyEntry*keyEntry=static_cast<KeyEntry*>(eventEntry);
- //Applytargetflags.
- int32_taction=keyEntry->action;
- int32_tflags=keyEntry->flags;
- //Publishthekeyevent.
- status=connection->inputPublisher.publishKeyEvent(keyEntry->deviceId,keyEntry->source,
- action,flags,keyEntry->keyCode,keyEntry->scanCode,
- keyEntry->metaState,keyEntry->repeatCount,keyEntry->downTime,
- keyEntry->eventTime);
- ......
- break;
- }
- ......
- }
- //Sendthedispatchsignal.
- status=connection->inputPublisher.sendDispatchSignal();
- ......
- }
Step 18. InputPublisher.publishKeyEvent
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
view plain
- status_tInputPublisher::publishKeyEvent(
- int32_tdeviceId,
- int32_tsource,
- int32_taction,
- int32_tflags,
- int32_tkeyCode,
- int32_tscanCode,
- int32_tmetaState,
- int32_trepeatCount,
- nsecs_tdownTime,
- nsecs_teventTime){
- ......
- status_tresult=publishInputEvent(AINPUT_EVENT_TYPE_KEY,deviceId,source);
- if(result<0){
- returnresult;
- }
- mSharedMessage->key.action=action;
- mSharedMessage->key.flags=flags;
- mSharedMessage->key.keyCode=keyCode;
- mSharedMessage->key.scanCode=scanCode;
- mSharedMessage->key.metaState=metaState;
- mSharedMessage->key.repeatCount=repeatCount;
- mSharedMessage->key.downTime=downTime;
- mSharedMessage->key.eventTime=eventTime;
- returnOK;
- }
这个匿名共享内存是什么时候创建的呢?前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封装成一个 Connection对象时,会调用它的initialize成员函数来执行一些初始化工作,就是在这个时候创建这个匿名共享内存的了:
view plain
- sp<Connection>connection=newConnection(inputChannel);
- status_tstatus=connection->initialize();
view plain
- status_tInputPublisher::initialize(){
- ......
- intashmemFd=mChannel->getAshmemFd();
- intresult=ashmem_get_size_region(ashmemFd);
- ......
- mAshmemSize=(size_t)result;
- mSharedMessage=static_cast<InputMessage*>(mmap(NULL,mAshmemSize,
- PROT_READ|PROT_WRITE,MAP_SHARED,ashmemFd,0));
- ......
- mPinned=true;
- mSharedMessage->consumed=false;
- returnreset();
- }
这个匿名共享内存mSharedMessage的作用是什么呢?原来,在InputChannel中,前向管道和反向管道的作用只是用来在Server端和Client端之间相互通知有事件发生了,但是具体是什么样的事件,还需要去读取这个匿名共享内存的内容才知道。前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 14中(InputChannel.openInputChannelPair)创建Server端和Client端的InputChannel对时,创建一个匿名共享内存,这个匿名共享内存有两个文件描述符同时指向它,其中一个放在Server端的InputChannel中,另外一个放在Client端的InputChannel中。这样,当InputDispatcher通过Server端的InputChannel的前向管道来通知Client端有键盘事件发生时,Client端只要通过它的InputChannel中的匿名共享内存文件描述符去读取匿名共享内存中的内容,就可以知道发生了什么事情了。有关匿名共享内存的相关知识,请参考Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划一文。
回到Step 17中,接下来就是调用InputPublisher的成员函数sendDispatchSignal来通知Activity窗口处理键盘事件了。
Step 19.InputPublishe.sendDispatchSignal
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
view plain
- status_tInputPublisher::sendDispatchSignal(){
- ......
- mWasDispatched=true;
- returnmChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
- }
Step 20. InputChannel.sendSignal
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
view plain
- status_tInputChannel::sendSignal(charsignal){
- ssize_tnWrite;
- do{
- nWrite=::write(mSendPipeFd,&signal,1);
- }while(nWrite==-1&&errno==EINTR);
- if(nWrite==1){
- ......
- returnOK;
- }
- return-errno;
- }
在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。
Step 21.NativeInputQueue.handleReceiveCallback
这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
view plain
- 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文件中:
view plain
- status_tInputConsumer::receiveDispatchSignal(){
- ......
- charsignal;
- status_tresult=mChannel->receiveSignal(&signal);
- if(result){
- returnresult;
- }
- if(signal!=INPUT_SIGNAL_DISPATCH){
- ......
- returnUNKNOWN_ERROR;
- }
- returnOK;
- }
InputChannel类的receiveSignal函数也是定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
view plain
- status_tInputChannel::receiveSignal(char*outSignal){
- ssize_tnRead;
- do{
- nRead=::read(mReceivePipeFd,outSignal,1);
- }while(nRead==-1&&errno==EINTR);
- if(nRead==1){
- ......
- returnOK;
- }
- ......
- return-errno;
- }
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
view plain- 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;
更多相关文章
- Android(安卓)事件处理
- ListView与Button的共存问题解决
- Android利用ViewFlipper实现屏幕切换动画效果(上)
- android 给动画添加结束监听事件
- Android(安卓)GUI系统学习1:Gralloc
- Android之通过ContentProvider实现两个app(进程间)间通信以及函
- Ubuntu下Android(安卓)JNI初步学习之——搭建相关环境和测试Demo
- [已解决]Android(安卓)ListView EditView 获取焦点问题
- Android(安卓)SO逆向-流程控制语句及表达式运算