`
yanlijun250
  • 浏览: 750985 次
文章分类
社区版块
存档分类
最新评论

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函数进一步处理:

  1. voidInputReader::loopOnce(){
  2. RawEventrawEvent;
  3. mEventHub->getEvent(&rawEvent);
  4. ......
  5. process(&rawEvent);
  6. }
Step 3. InputReader.process

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

  1. voidInputReader::process(constRawEvent*rawEvent){
  2. switch(rawEvent->type){
  3. caseEventHubInterface::DEVICE_ADDED:
  4. addDevice(rawEvent->deviceId);
  5. break;
  6. caseEventHubInterface::DEVICE_REMOVED:
  7. removeDevice(rawEvent->deviceId);
  8. break;
  9. caseEventHubInterface::FINISHED_DEVICE_SCAN:
  10. handleConfigurationChanged(rawEvent->when);
  11. break;
  12. default:
  13. consumeEvent(rawEvent);
  14. break;
  15. }
  16. }

当键盘事件发生时,rawEvent->type的值为EV_KEY,这是一个宏定义,具体可以参考bionic/libc/kernel/common/linux/input.h文件:

  1. #defineEV_KEY0x01
因此,接下来会调用consumeEvent函数进一步处理。

Step 4.InputReader.consumeEvent

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

  1. voidInputReader::consumeEvent(constRawEvent*rawEvent){
  2. int32_tdeviceId=rawEvent->deviceId;
  3. {//acquiredeviceregistryreaderlock
  4. RWLock::AutoRLock_rl(mDeviceRegistryLock);
  5. ssize_tdeviceIndex=mDevices.indexOfKey(deviceId);
  6. if(deviceIndex<0){
  7. LOGW("DiscardingeventforunknowndeviceId%d.",deviceId);
  8. return;
  9. }
  10. InputDevice*device=mDevices.valueAt(deviceIndex);
  11. if(device->isIgnored()){
  12. //LOGD("DiscardingeventforignoreddeviceId%d.",deviceId);
  13. return;
  14. }
  15. device->process(rawEvent);
  16. }//releasedeviceregistryreaderlock
  17. }
首先从rawEvent中取得触发键盘事件设备对象device,然后调用它的process函数进行处理。

Step 5. InputDevice.process

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

  1. voidInputDevice::process(constRawEvent*rawEvent){
  2. size_tnumMappers=mMappers.size();
  3. for(size_ti=0;i<numMappers;i++){
  4. InputMapper*mapper=mMappers[i];
  5. mapper->process(rawEvent);
  6. }
  7. }
这里的mMapper成员变量保存了一系列输入设备事件处理象,例如负责处理键盘事件的KeyboardKeyMapper对象、负责处理轨迹球事件的TrackballInputMapper对象以及负责处理触摸屏事件的TouchInputMapper对象, 它们是在InputReader类的成员函数createDevice中创建的。这里查询每一个InputMapper对象是否要对当前发生的事件进行处理。由于发生的是键盘事件,真正会对该事件进行处理的只有KeyboardKeyMapper对象。

Step 6.KeyboardInputMapper.process

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

  1. voidKeyboardInputMapper::process(constRawEvent*rawEvent){
  2. switch(rawEvent->type){
  3. caseEV_KEY:{
  4. int32_tscanCode=rawEvent->scanCode;
  5. if(isKeyboardOrGamepadKey(scanCode)){
  6. processKey(rawEvent->when,rawEvent->value!=0,rawEvent->keyCode,scanCode,
  7. rawEvent->flags);
  8. }
  9. break;
  10. }
  11. }
  12. }
这个函数首先会检查一下键盘扫描码是否正确,如果正确的话,就会调用processKey函数进一步处理。

Step 7.KeyboardInputMapper.processKey

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

  1. voidKeyboardInputMapper::processKey(nsecs_twhen,booldown,int32_tkeyCode,
  2. int32_tscanCode,uint32_tpolicyFlags){
  3. int32_tnewMetaState;
  4. nsecs_tdownTime;
  5. boolmetaStateChanged=false;
  6. {//acquirelock
  7. AutoMutex_l(mLock);
  8. if(down){
  9. //Rotatekeycodesaccordingtoorientationifneeded.
  10. //Note:getDisplayInfoisnon-reentrantsowecancontinueholdingthelock.
  11. if(mAssociatedDisplayId>=0){
  12. int32_torientation;
  13. if(!getPolicy()->getDisplayInfo(mAssociatedDisplayId,NULL,NULL,&orientation)){
  14. return;
  15. }
  16. keyCode=rotateKeyCode(keyCode,orientation);
  17. }
  18. //Addkeydown.
  19. ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
  20. if(keyDownIndex>=0){
  21. //keyrepeat,besuretousesamekeycodeasbeforeincaseofrotation
  22. keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
  23. }else{
  24. //keydown
  25. if((policyFlags&POLICY_FLAG_VIRTUAL)
  26. &&mContext->shouldDropVirtualKey(when,getDevice(),keyCode,scanCode)){
  27. return;
  28. }
  29. mLocked.keyDowns.push();
  30. KeyDown&keyDown=mLocked.keyDowns.editTop();
  31. keyDown.keyCode=keyCode;
  32. keyDown.scanCode=scanCode;
  33. }
  34. mLocked.downTime=when;
  35. }else{
  36. //Removekeydown.
  37. ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
  38. if(keyDownIndex>=0){
  39. //keyup,besuretousesamekeycodeasbeforeincaseofrotation
  40. keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
  41. mLocked.keyDowns.removeAt(size_t(keyDownIndex));
  42. }else{
  43. //keywasnotactuallydown
  44. LOGI("Droppingkeyupfromdevice%sbecausethekeywasnotdown."
  45. "keyCode=%d,scanCode=%d",
  46. getDeviceName().string(),keyCode,scanCode);
  47. return;
  48. }
  49. }
  50. int32_toldMetaState=mLocked.metaState;
  51. newMetaState=updateMetaState(keyCode,down,oldMetaState);
  52. if(oldMetaState!=newMetaState){
  53. mLocked.metaState=newMetaState;
  54. metaStateChanged=true;
  55. }
  56. downTime=mLocked.downTime;
  57. }//releaselock
  58. if(metaStateChanged){
  59. getContext()->updateGlobalMetaState();
  60. }
  61. getDispatcher()->notifyKey(when,getDeviceId(),AINPUT_SOURCE_KEYBOARD,policyFlags,
  62. down?AKEY_EVENT_ACTION_DOWN:AKEY_EVENT_ACTION_UP,
  63. AKEY_EVENT_FLAG_FROM_SYSTEM,keyCode,scanCode,newMetaState,downTime);
  64. }
这个函数首先对对按键作一些处理,例如,当某一个DPAD键被按下时,根据当时屏幕方向的不同,它所表示的意义也不同,因此,这里需要根据当时屏幕的方向来调整键盘码:
  1. //Rotatekeycodesaccordingtoorientationifneeded.
  2. //Note:getDisplayInfoisnon-reentrantsowecancontinueholdingthelock.
  3. if(mAssociatedDisplayId>=0){
  4. int32_torientation;
  5. if(!getPolicy()->getDisplayInfo(mAssociatedDisplayId,NULL,NULL,&orientation)){
  6. return;
  7. }
  8. keyCode=rotateKeyCode(keyCode,orientation);
  9. }
如果这个键是一直按着不放的,不管屏幕的方向如何,必须保证后面的键盘码和前面的一样:

  1. //Addkeydown.
  2. ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
  3. if(keyDownIndex>=0){
  4. //keyrepeat,besuretousesamekeycodeasbeforeincaseofrotation
  5. keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
  6. }else{
  7. //keydown
  8. if((policyFlags&POLICY_FLAG_VIRTUAL)
  9. &&mContext->shouldDropVirtualKey(when,getDevice(),keyCode,scanCode)){
  10. return;
  11. }
  12. mLocked.keyDowns.push();
  13. KeyDown&keyDown=mLocked.keyDowns.editTop();
  14. keyDown.keyCode=keyCode;
  15. keyDown.scanCode=scanCode;
  16. }
如果是第一次按下某个键,还必须把它保存在mLocked.keyDowns里面,就是为了处理上面讲的当这个键盘一直按着不放的时候屏幕方向发生改变的情况。
如果是松开键盘上的某个键,就把它从mLocked.keyDowns里面删除:

  1. //Removekeydown.
  2. ssize_tkeyDownIndex=findKeyDownLocked(scanCode);
  3. if(keyDownIndex>=0){
  4. //keyup,besuretousesamekeycodeasbeforeincaseofrotation
  5. keyCode=mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
  6. mLocked.keyDowns.removeAt(size_t(keyDownIndex));
  7. }else{
  8. //keywasnotactuallydown
  9. LOGI("Droppingkeyupfromdevice%sbecausethekeywasnotdown."
  10. "keyCode=%d,scanCode=%d",
  11. getDeviceName().string(),keyCode,scanCode);
  12. return;
  13. }
当然,对键盘事件的这些处理不是本文的重点,本文的重点是分析从键盘事件到当前激活的Activity窗口接收到这个键盘消息的过程。

最后,KeyboardInputMappger函数通知InputDispatcher,有键盘事件发生了:

  1. getDispatcher()->notifyKey(when,getDeviceId(),AINPUT_SOURCE_KEYBOARD,policyFlags,
  2. down?AKEY_EVENT_ACTION_DOWN:AKEY_EVENT_ACTION_UP,
  3. AKEY_EVENT_FLAG_FROM_SYSTEM,keyCode,scanCode,newMetaState,downTime);
Step 8. InputDispatcher.notifyKey

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

  1. voidInputDispatcher::notifyKey(nsecs_teventTime,int32_tdeviceId,int32_tsource,
  2. uint32_tpolicyFlags,int32_taction,int32_tflags,
  3. int32_tkeyCode,int32_tscanCode,int32_tmetaState,nsecs_tdownTime){
  4. ......
  5. if(!validateKeyEvent(action)){
  6. return;
  7. }
  8. /*Accordingtohttp://source.android.com/porting/keymaps_keyboard_input.html
  9. *Keydefinitions:KeydefinitionsfollowthesyntaxkeySCANCODEKEYCODE[FLAGS...],
  10. *whereSCANCODEisanumber,KEYCODEisdefinedinyourspecifickeylayoutfile
  11. *(android.keylayout.xxx),andpotentialFLAGSaredefinedasfollows:
  12. *SHIFT:Whilepressed,theshiftkeymodifierisset
  13. *ALT:Whilepressed,thealtkeymodifierisset
  14. *CAPS:Whilepressed,thecapslockkeymodifierisset
  15. *SinceKeyEvent.javadoesn'tcheckifCaplockisONandwedon'thavea
  16. *modiferstateforcaplock,wewillnotsupportit.
  17. */
  18. if(policyFlags&POLICY_FLAG_ALT){
  19. metaState|=AMETA_ALT_ON|AMETA_ALT_LEFT_ON;
  20. }
  21. if(policyFlags&POLICY_FLAG_ALT_GR){
  22. metaState|=AMETA_ALT_ON|AMETA_ALT_RIGHT_ON;
  23. }
  24. if(policyFlags&POLICY_FLAG_SHIFT){
  25. metaState|=AMETA_SHIFT_ON|AMETA_SHIFT_LEFT_ON;
  26. }
  27. policyFlags|=POLICY_FLAG_TRUSTED;
  28. mPolicy->interceptKeyBeforeQueueing(eventTime,deviceId,action,/*byref*/flags,
  29. keyCode,scanCode,/*byref*/policyFlags);
  30. boolneedWake;
  31. {//acquirelock
  32. AutoMutex_l(mLock);
  33. int32_trepeatCount=0;
  34. KeyEntry*newEntry=mAllocator.obtainKeyEntry(eventTime,
  35. deviceId,source,policyFlags,action,flags,keyCode,scanCode,
  36. metaState,repeatCount,downTime);
  37. needWake=enqueueInboundEventLocked(newEntry);
  38. }//releaselock
  39. if(needWake){
  40. mLooper->wake();
  41. }
  42. }
函数首先是调用validateKeyEvent函数来验证action参数是否正确:

  1. staticboolisValidKeyAction(int32_taction){
  2. switch(action){
  3. caseAKEY_EVENT_ACTION_DOWN:
  4. caseAKEY_EVENT_ACTION_UP:
  5. returntrue;
  6. default:
  7. returnfalse;
  8. }
  9. }
  10. staticboolvalidateKeyEvent(int32_taction){
  11. if(!isValidKeyAction(action)){
  12. LOGE("Keyeventhasinvalidactioncode0x%x",action);
  13. returnfalse;
  14. }
  15. returntrue;
  16. }
正确的action参数的值只能为AKEY_EVENT_ACTION_DOWN(按下)或者AKEY_EVENT_ACTION_UP(松开)。

参数action检查通过后,还通过policyFlags参数来检查一下同时是否有ALT和SHIFT键被按下:

  1. if(policyFlags&POLICY_FLAG_ALT){
  2. metaState|=AMETA_ALT_ON|AMETA_ALT_LEFT_ON;
  3. }
  4. if(policyFlags&POLICY_FLAG_ALT_GR){
  5. metaState|=AMETA_ALT_ON|AMETA_ALT_RIGHT_ON;
  6. }
  7. if(policyFlags&POLICY_FLAG_SHIFT){
  8. metaState|=AMETA_SHIFT_ON|AMETA_SHIFT_LEFT_ON;
  9. }
最后,调用enqueueInboundEventLocked函数把这个按键事件封装成一个KeyEntry结构加入到InputDispatcher类的mInboundQueue队列中去:

  1. boolInputDispatcher::enqueueInboundEventLocked(EventEntry*entry){
  2. boolneedWake=mInboundQueue.isEmpty();
  3. mInboundQueue.enqueueAtTail(entry);
  4. switch(entry->type){
  5. caseEventEntry::TYPE_KEY:{
  6. KeyEntry*keyEntry=static_cast<KeyEntry*>(entry);
  7. if(isAppSwitchKeyEventLocked(keyEntry)){
  8. if(keyEntry->action==AKEY_EVENT_ACTION_DOWN){
  9. mAppSwitchSawKeyDown=true;
  10. }elseif(keyEntry->action==AKEY_EVENT_ACTION_UP){
  11. if(mAppSwitchSawKeyDown){
  12. <spanstyle="white-space:pre"></span>......
  13. mAppSwitchDueTime=keyEntry->eventTime+APP_SWITCH_TIMEOUT;
  14. mAppSwitchSawKeyDown=false;
  15. needWake=true;
  16. }
  17. }
  18. }
  19. break;
  20. }
  21. }
  22. returnneedWake;
  23. }
从这个函数我们可以看出,在两种情况下,它的返回值为true,一是当加入该键盘事件到mInboundQueue之前,mInboundQueue为空,这表示InputDispatccherThread线程正在睡眠等待InputReaderThread线程的唤醒,因此,它返回true表示要唤醒InputDispatccherThread线程;二是加入该键盘事件到mInboundQueue之前,mInboundQueue不为空,但是此时用户按下的是Home键,按下Home键表示要切换App,我们知道,在切换App时,新的App会把它的键盘消息接收通道注册到InputDispatcher中去,并且会等待InputReader的唤醒,因此,在这种情况下,也需要返回true,表示要唤醒InputDispatccherThread线程。如果不是这两种情况,那么就说明InputDispatccherThread线程现在正在处理前面的键盘事件,不需要唤醒它。

回到前面的notifyKey函数中,根据enqueueInboundEventLocked函数的返回值来决定是否要唤醒InputDispatccherThread线程:

  1. if(needWake){
  2. mLooper->wake();
  3. }
这里,假设needWake为true,于是,就会调用mLooper对象的wake函数来唤醒InputDispatccherThread线程了。

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文件中:

  1. voidInputDispatcher::dispatchOnce(){
  2. nsecs_tkeyRepeatTimeout=mPolicy->getKeyRepeatTimeout();
  3. nsecs_tkeyRepeatDelay=mPolicy->getKeyRepeatDelay();
  4. nsecs_tnextWakeupTime=LONG_LONG_MAX;
  5. {//acquirelock
  6. AutoMutex_l(mLock);
  7. dispatchOnceInnerLocked(keyRepeatTimeout,keyRepeatDelay,&nextWakeupTime);
  8. ......
  9. }//releaselock
  10. ......
  11. }
它调用dispatchOnceInnerLocked函数来进一步处理这个键盘事件。

Step 11.InputDispatcher.dispatchOnceInnerLocked

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

  1. voidInputDispatcher::dispatchOnceInnerLocked(nsecs_tkeyRepeatTimeout,
  2. nsecs_tkeyRepeatDelay,nsecs_t*nextWakeupTime){
  3. ......
  4. //Readytostartanewevent.
  5. //Ifwedon'talreadyhaveapendingevent,gograbone.
  6. if(!mPendingEvent){
  7. if(mInboundQueue.isEmpty()){
  8. ......
  9. }else{
  10. //Inboundqueuehasatleastoneentry.
  11. EventEntry*entry=mInboundQueue.headSentinel.next;
  12. ......
  13. mInboundQueue.dequeue(entry);
  14. mPendingEvent=entry;
  15. }
  16. ......
  17. }
  18. ......
  19. switch(mPendingEvent->type){
  20. ......
  21. caseEventEntry::TYPE_KEY:{
  22. KeyEntry*typedEntry=static_cast<KeyEntry*>(mPendingEvent);
  23. ......
  24. done=dispatchKeyLocked(currentTime,typedEntry,keyRepeatTimeout,
  25. &dropReason,nextWakeupTime);
  26. break;
  27. }
  28. ......
  29. }
  30. ......
  31. }

我们忽略了这个函数的次要逻辑,主要关注键盘事件的主要处理流程。首先,如果前面发生的键盘事件都已经处理完毕,那么这里的mPendingEvent就为NULL,又因为前面我们把刚刚发生的键盘事件加入了mInboundQueue队列,因此,这里mInboundQueue不为NULL,于是,这里就把mInboundQueue队列中的键盘事件取出来,放在mPendingEvent变量中:

  1. mInboundQueue.dequeue(entry);
  2. mPendingEvent=entry;
由于这里发生的是键盘事件,即mPendingEvent->type的值为EventEntry::TYPE_KEY,于是,在接下来的switch语句中就会执行dispatchKeyLocked函数来分发键盘消息。

Step 12.InputDispatcher.dispatchKeyLocked

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

  1. boolInputDispatcher::dispatchKeyLocked(
  2. nsecs_tcurrentTime,KeyEntry*entry,nsecs_tkeyRepeatTimeout,
  3. DropReason*dropReason,nsecs_t*nextWakeupTime){
  4. ......
  5. //Identifytargets.
  6. if(!mCurrentInputTargetsValid){
  7. int32_tinjectionResult=findFocusedWindowTargetsLocked(currentTime,
  8. entry,nextWakeupTime);
  9. ......
  10. }
  11. //Dispatchthekey.
  12. dispatchEventToCurrentInputTargetsLocked(currentTime,entry,false);
  13. returntrue;
  14. }
InputDispatcher类中的mCurrentInputTargetsValid成员变量表示InputDispatcher是否已经标志出谁是当前激活的Activity窗口,如果没有,就需要通过findFocusedWindowTargetsLocked函数来把它找出来。当把当前激活的Activity窗口找出来以后,接下来就调用dispatchEventToCurrentInputTargetsLocked函数把键盘事件分发给它了。

我们先来看一InputDispatcher是如何找到当前激活的Activity窗口的,然后再分析它把键盘事件分发给当前激活Activity窗口的过程。

Step 13.InputDispatcher.findFocusedWindowTargetsLocked

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

  1. int32_tInputDispatcher::findFocusedWindowTargetsLocked(nsecs_tcurrentTime,
  2. constEventEntry*entry,nsecs_t*nextWakeupTime){
  3. mCurrentInputTargets.clear();
  4. int32_tinjectionResult;
  5. //Ifthereisnocurrentlyfocusedwindowandnofocusedapplication
  6. //thendroptheevent.
  7. if(!mFocusedWindow){
  8. if(mFocusedApplication){
  9. ......
  10. injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
  11. mFocusedApplication,NULL,nextWakeupTime);
  12. gotoUnresponsive;
  13. }
  14. ......
  15. injectionResult=INPUT_EVENT_INJECTION_FAILED;
  16. gotoFailed;
  17. }
  18. //Checkpermissions.
  19. if(!checkInjectionPermission(mFocusedWindow,entry->injectionState)){
  20. injectionResult=INPUT_EVENT_INJECTION_PERMISSION_DENIED;
  21. gotoFailed;
  22. }
  23. //Ifthecurrentlyfocusedwindowispausedthenkeepwaiting.
  24. if(mFocusedWindow->paused){
  25. ......
  26. injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
  27. mFocusedApplication,mFocusedWindow,nextWakeupTime);
  28. gotoUnresponsive;
  29. }
  30. //Ifthecurrentlyfocusedwindowisstillworkingonpreviouseventsthenkeepwaiting.
  31. if(!isWindowFinishedWithPreviousInputLocked(mFocusedWindow)){
  32. ......
  33. injectionResult=handleTargetsNotReadyLocked(currentTime,entry,
  34. mFocusedApplication,mFocusedWindow,nextWakeupTime);
  35. gotoUnresponsive;
  36. }
  37. //Success!Outputtargets.
  38. injectionResult=INPUT_EVENT_INJECTION_SUCCEEDED;
  39. addWindowTargetLocked(mFocusedWindow,InputTarget::FLAG_FOREGROUND,BitSet32(0));
  40. ......
  41. returninjectionResult;
  42. }
回忆前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 9中,当前处于激活状态的应用程序会通过调用InputDispatcher类setInputWindows函数把把当前获得焦点的Activity窗口设置到mFocusedWindow中去,因此,这里的mFocusedWindow不为NULL,于是,就通过了第一个if语句的检查。

第二个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文件中:

  1. voidInputDispatcher::addWindowTargetLocked(constInputWindow*window,int32_ttargetFlags,
  2. BitSet32pointerIds){
  3. mCurrentInputTargets.push();
  4. InputTarget&target=mCurrentInputTargets.editTop();
  5. target.inputChannel=window->inputChannel;
  6. target.flags=targetFlags;
  7. target.xOffset=-window->frameLeft;
  8. target.yOffset=-window->frameTop;
  9. target.pointerIds=pointerIds;
  10. }
这个函数简单,就是把传进来的参数window添加到mCurrentInputTargets中去就完事了,后面InputDispatcher就会从mCurrentInputTargets中取出恰当的Activity窗口,然后把键盘事件分发给它。

回到Step 12中的dispatchKeyLocked函数,它接下来就调用dispatchEventToCurrentInputTargetsLocked来进一步处理了。

Step 15.InputDispatcher.dispatchEventToCurrentInputTargetsLocked

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

  1. voidInputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_tcurrentTime,
  2. EventEntry*eventEntry,boolresumeWithAppendedMotionSample){
  3. ......
  4. for(size_ti=0;i<mCurrentInputTargets.size();i++){
  5. constInputTarget&inputTarget=mCurrentInputTargets.itemAt(i);
  6. ssize_tconnectionIndex=getConnectionIndexLocked(inputTarget.inputChannel);
  7. if(connectionIndex>=0){
  8. sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex);
  9. prepareDispatchCycleLocked(currentTime,connection,eventEntry,&inputTarget,
  10. resumeWithAppendedMotionSample);
  11. }else{
  12. ......
  13. }
  14. }
这个函数的实现也比较简单,前面我们已经把当前需要接受键盘事件的Activity窗口添加到mCurrentInputTargets中去了,因此,这里就分别把它们取出来,然后调用prepareDispatchCycleLocked函数把键盘事件分发给它们处理。

前面我们在分析应用程序注册键盘消息接收通道的过程时,在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文件中:

  1. voidInputDispatcher::prepareDispatchCycleLocked(nsecs_tcurrentTime,
  2. constsp<Connection>&connection,EventEntry*eventEntry,constInputTarget*inputTarget,
  3. boolresumeWithAppendedMotionSample){
  4. ......
  5. //Resumethedispatchcyclewithafreshlyappendedmotionsample.
  6. //Firstwecheckthatthelastdispatchentryintheoutboundqueueisforthesame
  7. //motioneventtowhichweappendedthemotionsample.Ifwefindsuchadispatch
  8. //entry,andifitiscurrentlyinprogressthenwetrytostreamthenewsample.
  9. boolwasEmpty=connection->outboundQueue.isEmpty();
  10. if(!wasEmpty&&resumeWithAppendedMotionSample){
  11. ......
  12. return;
  13. }
  14. //Thisisanewevent.
  15. //Enqueueanewdispatchentryontotheoutboundqueueforthisconnection.
  16. DispatchEntry*dispatchEntry=mAllocator.obtainDispatchEntry(eventEntry,//incrementsref
  17. inputTarget->flags,inputTarget->xOffset,inputTarget->yOffset);
  18. ......
  19. //Enqueuethedispatchentry.
  20. connection->outboundQueue.enqueueAtTail(dispatchEntry);
  21. //Iftheoutboundqueuewaspreviouslyempty,startthedispatchcyclegoing.
  22. if(wasEmpty){
  23. ......
  24. startDispatchCycleLocked(currentTime,connection);
  25. }
  26. }

在开始处理键盘事件之前,这个函数会检查一下传进来的参数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文件中:

  1. voidInputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime,
  2. constsp<Connection>&connection){
  3. ......
  4. DispatchEntry*dispatchEntry=connection->outboundQueue.headSentinel.next;
  5. //Markthedispatchentryasinprogress.
  6. dispatchEntry->inProgress=true;
  7. //Updatetheconnection'sinputstate.
  8. EventEntry*eventEntry=dispatchEntry->eventEntry;
  9. ......
  10. //Publishtheevent.
  11. status_tstatus;
  12. switch(eventEntry->type){
  13. caseEventEntry::TYPE_KEY:{
  14. KeyEntry*keyEntry=static_cast<KeyEntry*>(eventEntry);
  15. //Applytargetflags.
  16. int32_taction=keyEntry->action;
  17. int32_tflags=keyEntry->flags;
  18. //Publishthekeyevent.
  19. status=connection->inputPublisher.publishKeyEvent(keyEntry->deviceId,keyEntry->source,
  20. action,flags,keyEntry->keyCode,keyEntry->scanCode,
  21. keyEntry->metaState,keyEntry->repeatCount,keyEntry->downTime,
  22. keyEntry->eventTime);
  23. ......
  24. break;
  25. }
  26. ......
  27. }
  28. //Sendthedispatchsignal.
  29. status=connection->inputPublisher.sendDispatchSignal();
  30. ......
  31. }
这个函数主要围绕传进来的Connection对象做两件事情,一是从它的outboundQueue队列中取出当前需要处理的键盘事件,然后把这个事件记录在它的内部对象inputPublisher中,二是通过它的内部对象inputPublisher通知它所关联的Activity窗口,现在有键盘事件需要处理了。第一件事情是通过调用它的InputPublisher对象的publishKeyEvent函数来完成的,而第二件事情是通过调用它的InputPublisher对象的sendDispatchSignal来完成的。我们先来看InputPublisher的成员函数publishKeyEvent的实现,然后再回来分析它的另外一个成员函数sendDispatchSignal的实现。

Step 18. InputPublisher.publishKeyEvent

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

  1. status_tInputPublisher::publishKeyEvent(
  2. int32_tdeviceId,
  3. int32_tsource,
  4. int32_taction,
  5. int32_tflags,
  6. int32_tkeyCode,
  7. int32_tscanCode,
  8. int32_tmetaState,
  9. int32_trepeatCount,
  10. nsecs_tdownTime,
  11. nsecs_teventTime){
  12. ......
  13. status_tresult=publishInputEvent(AINPUT_EVENT_TYPE_KEY,deviceId,source);
  14. if(result<0){
  15. returnresult;
  16. }
  17. mSharedMessage->key.action=action;
  18. mSharedMessage->key.flags=flags;
  19. mSharedMessage->key.keyCode=keyCode;
  20. mSharedMessage->key.scanCode=scanCode;
  21. mSharedMessage->key.metaState=metaState;
  22. mSharedMessage->key.repeatCount=repeatCount;
  23. mSharedMessage->key.downTime=downTime;
  24. mSharedMessage->key.eventTime=eventTime;
  25. returnOK;
  26. }
这个函数主要就是把键盘事件记录在InputPublisher类的成员变量mSharedMessage中了,这个mSharedMessage成员变量指向的是一个匿名共享内存。

这个匿名共享内存是什么时候创建的呢?前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封装成一个 Connection对象时,会调用它的initialize成员函数来执行一些初始化工作,就是在这个时候创建这个匿名共享内存的了:

  1. sp<Connection>connection=newConnection(inputChannel);
  2. status_tstatus=connection->initialize();
我们来看一下这个initialize函数的实现,它定义在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_tInputPublisher::initialize(){
  2. ......
  3. intashmemFd=mChannel->getAshmemFd();
  4. intresult=ashmem_get_size_region(ashmemFd);
  5. ......
  6. mAshmemSize=(size_t)result;
  7. mSharedMessage=static_cast<InputMessage*>(mmap(NULL,mAshmemSize,
  8. PROT_READ|PROT_WRITE,MAP_SHARED,ashmemFd,0));
  9. ......
  10. mPinned=true;
  11. mSharedMessage->consumed=false;
  12. returnreset();
  13. }
InputPublisher的成员变量mChannel就是指注册在InputDispatcher中的Server端InputChannel了。我们知道,这个InputChannel除了拥有一个反向管道的读端文件描述符和一个前向管道的写端文件描述符之后,还有一个匿名共享文件描述符,这个匿名共享文件描述符就是用来创建匿名共享内存mSharedMessage的了。

这个匿名共享内存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文件中:

  1. status_tInputPublisher::sendDispatchSignal(){
  2. ......
  3. mWasDispatched=true;
  4. returnmChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
  5. }
这个函数很简单,它通过调用内部成员变量mChannel的sendSignal函数来通知相应的Activity窗口来处理键盘事件。

Step 20. InputChannel.sendSignal

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

  1. status_tInputChannel::sendSignal(charsignal){
  2. ssize_tnWrite;
  3. do{
  4. nWrite=::write(mSendPipeFd,&signal,1);
  5. }while(nWrite==-1&&errno==EINTR);
  6. if(nWrite==1){
  7. ......
  8. returnOK;
  9. }
  10. return-errno;
  11. }
这里所谓的发送信号通知,其实是通过向其内部一个管道的写端写入一个字符来实现的。前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),它把一个InputChannel注册到应用程序主线程中的Looper对象中,然后应用程序的主线程就通过这个Looper对象睡眠等待在这个InputChannel中的前向管道中有新的内容可读了,这里的mSendPipeFd就是对应这个前向管道的写端。现在既然向这个前向管道的写端写入新的内容了,于是,应用程序的主线程就被唤醒了。

在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。

Step 21.NativeInputQueue.handleReceiveCallback

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

  1. intNativeInputQueue::handleReceiveCallback(intreceiveFd,intevents,void*data){
  2. NativeInputQueue*q=static_cast<NativeInputQueue*>(data);
  3. JNIEnv*env=AndroidRuntime::getJNIEnv();
  4. sp<Connection>connection;
  5. InputEvent*inputEvent;
  6. jobjectinputHandlerObjLocal;
  7. jlongfinishedToken;
  8. {//acquirelock
  9. AutoMutex_l(q->mLock);
  10. ssize_tconnectionIndex=q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
  11. ......
  12. connection=q->mConnectionsByReceiveFd.valueAt(connectionIndex);
  13. ......
  14. status_tstatus=connection->inputConsumer.receiveDispatchSignal();
  15. if(status){
  16. ......
  17. return0;//removethecallback
  18. }
  19. ......
  20. status=connection->inputConsumer.consume(&connection->inputEventFactory,&inputEvent);
  21. ......
  22. finishedToken=generateFinishedToken(receiveFd,connection->id,connection->messageSeqNum);
  23. inputHandlerObjLocal=env->NewLocalRef(connection->inputHandlerObjGlobal);
  24. }//releaselock
  25. ......
  26. int32_tinputEventType=inputEvent->getType();
  27. jobjectinputEventObj;
  28. jmethodIDdispatchMethodId;
  29. switch(inputEventType){
  30. caseAINPUT_EVENT_TYPE_KEY:
  31. ......
  32. inputEventObj=android_view_KeyEvent_fromNative(env,
  33. static_cast<KeyEvent*>(inputEvent));
  34. dispatchMethodId=gInputQueueClassInfo.dispatchKeyEvent;
  35. break;
  36. }
  37. ......
  38. }
  39. ......
  40. env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
  41. dispatchMethodId,inputHandlerObjLocal,inputEventObj,
  42. jlong(finishedToken));
  43. ......
  44. return1;
  45. }

这个函数首先是通过参数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_tInputConsumer::receiveDispatchSignal(){
  2. ......
  3. charsignal;
  4. status_tresult=mChannel->receiveSignal(&signal);
  5. if(result){
  6. returnresult;
  7. }
  8. if(signal!=INPUT_SIGNAL_DISPATCH){
  9. ......
  10. returnUNKNOWN_ERROR;
  11. }
  12. returnOK;
  13. }
这个函数很简单,它通过它内部对象mChannel来从前向管道的读端读入一个字符,看看是否是前面的Step 20中写入的INPUT_SIGNAL_DISPATCH字符。

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

  1. status_tInputChannel::receiveSignal(char*outSignal){
  2. ssize_tnRead;
  3. do{
  4. nRead=::read(mReceivePipeFd,outSignal,1);
  5. }while(nRead==-1&&errno==EINTR);
  6. if(nRead==1){
  7. ......
  8. returnOK;
  9. }
  10. ......
  11. return-errno;
  12. }
Step 23.InputConsumer.consume

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

  1. status_tInputConsumer::consume(InputEventFactoryInterface*factory,InputEvent**outEvent){
  2. ......
  3. *outEvent=NULL;
  4. intashmemFd=mChannel->getAshmemFd();
  5. intresult=ashmem_pin_region(ashmemFd,0,0);
  6. ......
  7. if(mSharedMessage->consumed){
  8. ......
  9. returnINVALID_OPERATION;
  10. }
  11. //Acquirebut*neverrelease*thesemaphore.Contentiononthesemaphoreisusedtosignal
  12. //tothepublisherthatthemessagehasbeenconsumed(orisintheprocessofbeing
  13. //consumed).Eventuallythepublisherwillreinitializethesemaphoreforthenextmessage.
  14. result=sem_wait(&mSharedMessage->semaphore);
  15. ......
  16. mSharedMessage->consumed=true;
  17. switch(mSharedMessage->type){
  18. caseAINPUT_EVENT_TYPE_KEY:{
  19. KeyEvent*keyEvent=factory->createKeyEvent();
  20. if(!keyEvent)returnNO_MEMORY;
  21. populateKeyEvent(keyEvent);
  22. *outEvent=keyEvent;
  23. break;
  24. }
  25. ......
  26. }
  27. returnOK;
  28. }
这个函数很简单,只要对照前面的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));
Step 24. InputQueue.dispatchKeyEvent

这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中:

  1. publicfinalclassInputQueue{
  2. ......
  3. privatestaticvoiddispatchKeyEvent(InputHandlerinputHandler,
  4. KeyEventevent,longfinishedToken){
  5. RunnablefinishedCallback=FinishedCallback.obtain(finishedToken);
  6. inputHandler.handleKey(event,finishedCallback);
  7. }
  8. ......
  9. }
这个函数首先会创建一个FinishedCallback类型的对象finishedCallback,FinishedCallback是InputQueue的一个内部类,它继承于Runnable类。这个finishedCallback对象是提供给当前Activity窗口的,当它处理完毕键盘事件后,需要通过消息分发的方式来回调这个finishedCallback对象,以及InputQueue类处理一个手尾的工作,后面我们会分析到。

这里的inputHandler对象是在前面分析应用程序注册键盘消息接收通道的过程时,在Step 1(ViewRoot.setView)中传进来的:

  1. InputQueue.registerInputChannel(mInputChannel,mInputHandler,
  2. Looper.myQueue());
它是ViewRoot类的一个成员变量mInputHandler。因此,这里将调用ViewRoot类的内部对象mInputHandler的成员函数handleKey来处理键盘事件。

Step 25. InputHandler.handleKey

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatefinalInputHandlermInputHandler=newInputHandler(){
  5. publicvoidhandleKey(KeyEventevent,RunnablefinishedCallback){
  6. startInputEvent(finishedCallback);
  7. dispatchKey(event,true);
  8. }
  9. ......
  10. };
  11. ......
  12. }
这个函数首先调用其外部类ViewRoot的startInputEvent成员函数来把回调对象finishedCallback保存下来:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatevoidstartInputEvent(RunnablefinishedCallback){
  5. ......
  6. mFinishedCallback=finishedCallback;
  7. }
  8. ......
  9. }
然后再调用其外部类ViewRoot的dispatchKey成员函数来进一步处这个键盘事件。

Step 26.ViewRoot.dispatchKey

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatevoiddispatchKey(KeyEventevent,booleansendDone){
  5. ......
  6. Messagemsg=obtainMessage(DISPATCH_KEY);
  7. msg.obj=event;
  8. msg.arg1=sendDone?1:0;
  9. ......
  10. sendMessageAtTime(msg,event.getEventTime());
  11. }
  12. ......
  13. }
ViewRoot不是直接处理这个键盘事件,而是把作为一个消息(DISPATCH_KEY)它放到消息队列中去处理,这个消息最后由ViewRoot类的deliverKeyEvent成员函数来处理。
Step 27.ViewRoot.deliverKeyEvent

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatevoiddeliverKeyEvent(KeyEventevent,booleansendDone){
  5. //IfmViewisnull,wejustconsumethekeyeventbecauseitdoesn't
  6. //makesensetodoanythingelsewithit.
  7. booleanhandled=mView!=null
  8. ?mView.dispatchKeyEventPreIme(event):true;
  9. ......
  10. //Ifitispossibleforthiswindowtointeractwiththeinput
  11. //methodwindow,thenwewanttofirstdispatchourkeyevents
  12. //totheinputmethod.
  13. if(mLastWasImTarget){
  14. InputMethodManagerimm=InputMethodManager.peekInstance();
  15. if(imm!=null&&mView!=null){
  16. ......
  17. imm.dispatchKeyEvent(mView.getContext(),seq,event,
  18. mInputMethodCallback);
  19. return;
  20. }
  21. }
  22. ......
  23. }
  24. ......
  25. }
ViewRoot在把这个键盘事件分发给当前激活的Activity窗口处理之前,首先会调用InputMethodManager的dispatchKeyEvent成员函数来处理这个键盘事件。InputMethodManager处理完这个键盘事件后,再回调用这里的mInputMethodCallback对象的finishedEvent成员函数来把键盘事件分发给当前激活的Activity窗口处理。当然,在把这个键盘事件分发给InputMethodManager处理之前,ViewRoot也会先把这个键盘事件分发给当前激活的Activity窗口的dispatchKeyEventPreIme成员函数处理。

Step 28. InputMethodManager.dispatchKeyEvent

这个函数定义在frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java文件中。这是一个输入法相关的类,我们这里就不关注了,只要知道当输入法处理完成之后,它就会调用ViewRoot类的mInputMehtodCallback对象的finishedEvent成员函数。

Step 29. InputMethodCallack.finishedEvent

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. staticclassInputMethodCallbackextendsIInputMethodCallback.Stub{
  5. privateWeakReference<ViewRoot>mViewRoot;
  6. publicInputMethodCallback(ViewRootviewRoot){
  7. mViewRoot=newWeakReference<ViewRoot>(viewRoot);
  8. }
  9. publicvoidfinishedEvent(intseq,booleanhandled){
  10. finalViewRootviewRoot=mViewRoot.get();
  11. if(viewRoot!=null){
  12. viewRoot.dispatchFinishedEvent(seq,handled);
  13. }
  14. }
  15. ......
  16. }
  17. ......
  18. }
这个函数最终调用ViewRoot的dispatchFinishedEvent来进一步处理。

Step 30.ViewRoot.dispatchFinishedEvent

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. publicvoiddispatchFinishedEvent(intseq,booleanhandled){
  5. Messagemsg=obtainMessage(FINISHED_EVENT);
  6. msg.arg1=seq;
  7. msg.arg2=handled?1:0;
  8. sendMessage(msg);
  9. }
  10. ......
  11. }
和前面的Step 26一样,ViewRoot不是直接处理这个键盘事件,而是把它作为一个消息(FINISHED_EVENT)放在消息队列中去,最后,这个消息由ViewRoot的handleFinishedEvent函数来处理。

Step 31.ViewRoot.handleFinishedEvent

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. voidhandleFinishedEvent(intseq,booleanhandled){
  5. finalKeyEventevent=(KeyEvent)retrievePendingEvent(seq);
  6. ......
  7. if(event!=null){
  8. finalbooleansendDone=seq>=0;
  9. if(!handled){
  10. deliverKeyEventToViewHierarchy(event,sendDone);
  11. return;
  12. }elseif(sendDone){
  13. ......
  14. }else{
  15. ......
  16. }
  17. }
  18. }
  19. ......
  20. }
如果InputMethodManager没有处理这个键盘事件,那么ViewRoot就会调用deliverKeyEventToViewHierarchy函数来把这个键盘事件分发给当前激活的Activity窗口来处理。

Step 32.ViewRoot.deliverKeyEventToViewHierarchy

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatevoiddeliverKeyEventToViewHierarchy(KeyEventevent,booleansendDone){
  5. try{
  6. if(mView!=null&&mAdded){
  7. ......
  8. booleankeyHandled=mView.dispatchKeyEvent(event);
  9. }
  10. ......
  11. }finally{
  12. if(sendDone){
  13. finishInputEvent();
  14. }
  15. }
  16. }
  17. ......
  18. }
这个函数首先会调用ViewRoot类的成员变量mView的dispatchKeyEvent来处理这个键盘事件,然后最调用ViewRoot类的finishInputEvent来处理手尾工作。

ViewRoot类的成员变量mView的类型为DecorView,它是由ActivityThread类第一次Resume当前的Activity窗口时创建的,具体可以参考ActivityThread类的handleResumeActivity成员函数,这里就不关注了。

Step 33. DecorView.dispatchKeyEvent

这个函数定义在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java文件中,它是PhoneWindow类的一个内部类:

  1. publicclassPhoneWindowextendsWindowimplementsMenuBuilder.Callback{
  2. ......
  3. privatefinalclassDecorViewextendsFrameLayoutimplementsRootViewSurfaceTaker{
  4. ......
  5. @Override
  6. publicbooleandispatchKeyEvent(KeyEventevent){
  7. ......
  8. finalCallbackcb=getCallback();
  9. finalbooleanhandled=cb!=null&&mFeatureId<0?cb.dispatchKeyEvent(event)
  10. :super.dispatchKeyEvent(event);
  11. ......
  12. }
  13. ......
  14. }
  15. ......
  16. }
这里通过getCallback函数返回的是当前应用程序的激活的Activity窗口的Window.Callback接口,一般它不为NULL,因此,这个函数会调用Activity类的dispatchKeyEvent来处理这个键盘事件。
Step 34. Activity.dispatchKeyEvent

这个函数定义在frameworks/base/core/java/android/app/Activity.java文件中:

  1. publicclassActivityextendsContextThemeWrapper
  2. implementsLayoutInflater.Factory,
  3. Window.Callback,KeyEvent.Callback,
  4. OnCreateContextMenuListener,ComponentCallbacks{
  5. ......
  6. publicbooleandispatchKeyEvent(KeyEventevent){
  7. ......
  8. Viewdecor=mDecor;
  9. if(decor==null)decor=win.getDecorView();
  10. returnevent.dispatch(this,decor!=null
  11. ?decor.getKeyDispatcherState():null,this);
  12. }
  13. ......
  14. }
这里,Activity不是直接处理这个键盘事件,而是通过KeyEvent的dispatch转发一下。注意,KeyEvent的成中函数dispatch的第一个参数的类型是KeyEvent.Callback,而Activity实现了这个接口,因此,这里可以传this引用过去。

Step 35. KeyEvent.dispatch

这个函数定义在frameworks/base/core/java/android/view/KeyEvent.java文件中:

  1. publicclassKeyEventextendsInputEventimplementsParcelable{
  2. ......
  3. publicfinalbooleandispatch(Callbackreceiver,DispatcherStatestate,
  4. Objecttarget){
  5. switch(mAction){
  6. caseACTION_DOWN:{
  7. ......
  8. booleanres=receiver.onKeyDown(mKeyCode,this);
  9. ......
  10. returnres;
  11. }
  12. caseACTION_UP:
  13. ......
  14. returnreceiver.onKeyUp(mKeyCode,this);
  15. caseACTION_MULTIPLE:
  16. finalintcount=mRepeatCount;
  17. finalintcode=mKeyCode;
  18. if(receiver.onKeyMultiple(code,count,this)){
  19. returntrue;
  20. }
  21. ......
  22. returnfalse;
  23. }
  24. returnfalse;
  25. }
  26. ......
  27. }
这里就根据一个键是按下(ACTION_DOWN)、还是松开(ACTION_UP)或者是一个相同的键被多次按下和松开(ACTION_MULTIPLE)等不同事件类型来分别调用Activity的onKeyDown、onKeyUp和onKeyMultiple函数了。
Activity窗口处理完这个键盘事件后,层层返回,最后回到Step 32中,调用finishInputEvent事件来处理一些手尾工,下面我们将会看到这些手尾工是什么。

Step 36. ViewRoot.finishInputEvent

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. publicfinalclassViewRootextendsHandlerimplementsViewParent,
  2. View.AttachInfo.Callbacks{
  3. ......
  4. privatevoidfinishInputEvent(){
  5. ......
  6. if(mFinishedCallback!=null){
  7. mFinishedCallback.run();
  8. mFinishedCallback=null;
  9. }else{
  10. ......
  11. }
  12. }
  13. ......
  14. }

ViewRoot类里面的成员变量mFinishedCallback是在前面Step 25中由InputQueue设置的,它是一个Runnable对象,实际类型是定义在InputQueue的内部类FinishedCallback,因此,这里调用它的run方法时,接下来就会调用InputQueue的内部类FinishedCallback的run成员函数:

  1. publicfinalclassInputQueue{
  2. ......
  3. privatestaticclassFinishedCallbackimplementsRunnable{
  4. ......
  5. publicvoidrun(){
  6. synchronized(sLock){
  7. ......
  8. nativeFinished(mFinishedToken);
  9. ......
  10. }
  11. }
  12. ......
  13. }
  14. ......
  15. }
这里它调用外部类InputQueue的本地方法nativeFinished来进一步处理。

Step 37. InputQueue.nativeFinished

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

  1. staticvoidandroid_view_InputQueue_nativeFinished(JNIEnv*env,jclassclazz,
  2. jlongfinishedToken){
  3. status_tstatus=gNativeInputQueue.finished(
  4. env,finishedToken,false/*ignoreSpuriousFinish*/);
  5. ......
  6. }
这个函数只是简单只调用NativeInputQueue的finished方法来进一处处理。

Step 38. NativeInputQueue.finished

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

  1. status_tNativeInputQueue::finished(JNIEnv*env,jlongfinishedToken,boolignoreSpuriousFinish){
  2. int32_treceiveFd;
  3. uint16_tconnectionId;
  4. uint16_tmessageSeqNum;
  5. parseFinishedToken(finishedToken,&receiveFd,&connectionId,&messageSeqNum);
  6. {//acquirelock
  7. AutoMutex_l(mLock);
  8. ssize_tconnectionIndex=mConnectionsByReceiveFd.indexOfKey(receiveFd);
  9. ......
  10. sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex);
  11. ......
  12. connection->messageInProgress=false;
  13. status_tstatus=connection->inputConsumer.sendFinishedSignal();
  14. ......
  15. }//releaselock
  16. returnOK;
  17. }
这个函数最重要的参数便是finishedToken了,通过它可以获得之前通知Java层的InputQueue类来处理键盘事件的Connection对象,它的值是在上面的Step 21(NativeInputQueue.handleReceiveCallback)中生成的:

  1. finishedToken=generateFinishedToken(receiveFd,connection->id,connection->messageSeqNum);
函数generateFinishedToken的定义如下:

  1. jlongNativeInputQueue::generateFinishedToken(int32_treceiveFd,uint16_tconnectionId,
  2. uint16_tmessageSeqNum){
  3. return(jlong(receiveFd)<<32)|(jlong(connectionId)<<16)|jlong(messageSeqNum);
  4. }
它的实现很简单,只是把receiveFd(前向管道的读端文件描述符)、connectionId(Client端的InputChannel对应的Connection对象在NativeInputQueue中的索引)和messageSeqNum(键盘消息的序列号)三个数值通过移位的方式编码在一个jlong值里面,即编码在上面的finishedToken参数里面。

因此,在上面的finished函数里面,首先就是要对参数值finishedToken进行解码,把receiveFd、connectionId和messageSeqNum三个值分别取回来:

  1. parseFinishedToken(finishedToken,&receiveFd,&connectionId,&messageSeqNum);
parseFinishedToken的定义如下:

  1. voidNativeInputQueue::parseFinishedToken(jlongfinishedToken,
  2. int32_t*outReceiveFd,uint16_t*outConnectionId,uint16_t*outMessageIndex){
  3. *outReceiveFd=int32_t(finishedToken>>32);
  4. *outConnectionId=uint16_t(finishedToken>>16);
  5. *outMessageIndex=uint16_t(finishedToken);
  6. }
有了这个receiveFd和connectionId之后,就可以把相应的Connection对象取回来了:

  1. ssize_tconnectionIndex=mConnectionsByReceiveFd.indexOfKey(receiveFd);
  2. ......
  3. sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex);
接下来就是调用这个connection对象中的inputConsumer对象来发送信号通知Server端的InputChannel,应用程序这一侧处理完刚才发生的键盘事件了:

  1. status_tstatus=connection->inputConsumer.sendFinishedSignal();

Step 39. InputConsumer.sendFinishedSignal

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

  1. status_tInputConsumer::sendFinishedSignal(){
  2. ......
  3. returnmChannel->sendSignal(INPUT_SIGNAL_FINISHED);
  4. }
这个函数的实现很简单,只是调用其内部对象mChannel的sendSignal函数来执行发送信号的通知。前面我们已经说过,这里的mChannel的类型为InputChannel,它是注册在应用程序一侧的Client端InputChannel,它的成员函数sendSignal的定义我们在上面的Step 20中已经分析过了,这里不再详述,不过,这里和上面Step 20不一样的地方是,它里的通知方向是从反向管道的写端(在应用程序这一侧)到反向管道的读端(在InputDispatcher这一侧)。

前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18(InputDispatcher.registerInputChannel)中,说到InputDispatcher把一个反向管道的读端文件描述符添加到WindowManagerService所运行的线程中的Looper对象中去,然后就会在这个反向管道的读端上睡眠等待有这个管道有新的内容可读。现在,InputConsumer往这个反向管道写入新的内容了,于是,InputDispatcher就被唤醒过来了,唤醒过来后,它所调用的函数是InputDispatcher.handleReceiveCallback函数,这与前面的Step 21的逻辑是一样的。

Step 40. InputDispatcher.handleReceiveCallack

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

  1. intInputDispatcher::handleReceiveCallback(intreceiveFd,intevents,void*data){
  2. InputDispatcher*d=static_cast<InputDispatcher*>(data);
  3. {//acquirelock
  4. AutoMutex_l(d->mLock);
  5. ssize_tconnectionIndex=d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
  6. ......
  7. nsecs_tcurrentTime=now();
  8. sp<Connection>connection=d->mConnectionsByReceiveFd.valueAt(connectionIndex);
  9. ......
  10. status_tstatus=connection->inputPublisher.receiveFinishedSignal();
  11. if(status){
  12. ......
  13. return0;//removethecallback
  14. }
  15. d->finishDispatchCycleLocked(currentTime,connection);
  16. ......
  17. return1;
  18. }//releaselock
  19. }
这个函数首先是通过传进来的receiveFd参数(反向管道的读端文件描述符)的值取得相应的Connection对象:

  1. ssize_tconnectionIndex=d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
  2. ......
  3. sp<Connection>connection=d->mConnectionsByReceiveFd.valueAt(connectionIndex);
然后通过调用这个connection对象的内部对象inputPublisher的receiveFinishedSignal函数来确认是否真的收到键盘事件处理完成的信号,确认之后,就会调用InputDispatcher对象d的finishDispatchCycleLocked函数来执行一些善后工作。下面我们就依次分析这两个过程。

Step 41. InputPublisher.receiverFinishedSignal

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

  1. status_tInputPublisher::receiveFinishedSignal(){
  2. ....
  3. charsignal;
  4. status_tresult=mChannel->receiveSignal(&signal);
  5. if(result){
  6. returnresult;
  7. }
  8. if(signal!=INPUT_SIGNAL_FINISHED){
  9. .......
  10. returnUNKNOWN_ERROR;
  11. }
  12. returnOK;
  13. }
这里的逻辑和前面的Step 22中NativeInputQueue确认是否真的收到键盘事件分发的信号的逻辑是一致的,都是通过InputChannel的receiveSignal函数来确认是否在管道中收到了某一个约定的字符值,不过,这里约定的字符值为INPUT_SIGNAL_FINISHED。

回到前面的Step 40中,确认了是真的收到了键盘事件处理完成的信号后,就调用InputDispatcher的finishDispatchCycleLocked函数来执行一些善后工作了。

Step 42. InputDispatcher.finishDispatchCycleLocked
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. voidInputDispatcher::finishDispatchCycleLocked(nsecs_tcurrentTime,
  2. constsp<Connection>&connection){
  3. ......
  4. //Notifyothersystemcomponents.
  5. onDispatchCycleFinishedLocked(currentTime,connection);
  6. //Resetthepublishersincetheeventhasbeenconsumed.
  7. //Wedothisnowsothatthepublishercanreleasesomeofitsinternalresources
  8. //whilewaitingforthenextdispatchcycletobegin.
  9. status_tstatus=connection->inputPublisher.reset();
  10. ......
  11. startNextDispatchCycleLocked(currentTime,connection);
  12. }
这个函数主要就是做了三件事情:

一是通知其它系统,InputDispatcher完成了一次键盘事件的处理:

  1. //Notifyothersystemcomponents.
  2. onDispatchCycleFinishedLocked(currentTime,connection);
二是调用相应的connection对象的内部对象inputPublisher来的reset函数来回收一些资源,它里面其实就是释放前面在Step 18(InputPublisher.publishKeyEvent)使用的匿名共享内存了:

  1. //Resetthepublishersincetheeventhasbeenconsumed.
  2. //Wedothisnowsothatthepublishercanreleasesomeofitsinternalresources
  3. //whilewaitingforthenextdispatchcycletobegin.
  4. status_tstatus=connection->inputPublisher.reset();
三是调用InputDispatcher的startNextDispatchCycleLocked函数来处理下一个键盘事件:

  1. startNextDispatchCycleLocked(currentTime,connection);
因为正在处理当前这个键盘事件的时候,很有可能又同时发生了其它的键盘事件,因此,这里InputDispatcher还不能停下来,需要继续调用startNextDispatchCycleLocked继续处理键盘事件,不过下一个键盘事件的处理过程和我们现在分析的过程就是一样的了。

至此,InputManager分发键盘消息给应用程序的过程就分析完成了,这是一个比较复杂的过程,不过,只要我们抓住主要的线索,就不难理解了,现在我们就小结一下这个过程的四个主要线索:

A. 键盘事件发生,InputManager中的InputReader被唤醒,此前InputReader睡眠在/dev/input/event0这个设备文件上;

B.InputReader被唤醒后,它接着唤醒InputManager中的InputDispatcher,此前InputDispatcher睡眠在InputManager所运行的线程中的Looper对象里面的管道的读端上;

C. InputDispatcher被唤醒后,它接着唤醒应用程序的主线程来处理这个键盘事件,此前应用程序的主线程睡眠在Client端InputChannel中的前向管道的读端上;

D. 应用程序处理处理键盘事件之后,它接着唤醒InputDispatcher来执行善后工作,此前InputDispatcher睡眠在Server端InputChannel的反向管道的读端上,注意这里与第二个线索处的区别。

----

下接:Android应用程序键盘(Keyboard)消息处理机制分析(四)

转自:老罗的Android应用程序键盘(Keyboard)消息处理机制分析

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics