主要涉及的文件有:

WindowManagerService.java frameworks\base\services\java\com\android\server\

PhoneWindow.java frameworks\policies\base\phone\com\android\internal\policy\impl

KeyInputQueue.java frameworks\base\services\java\com\android\server

com_android_server_KeyInputQueue.cpp frameworks\base\services\jni

EventHub.cpp frameworks\base\libs\ui


WindowManagerService.java主要有两个线程,一个负责分发按键的InputDisapath Thread,另一个负责从底层读取按键消息InputDeviceRender Thread。

WindowManagerService.java的成员类KeyQ(),负责获取各种按键设备的状态,它继承于KeyInputQueue类。通过线程InputDeviceRender Thread的readEvent对按键消息不停读取,然后调用KeyQ实例化后的processEvent函数告诉该按键是否应该传给上层。接着WindowManagerService通过InputDisPatch Thread在按键消息队列里取出,并进行分发。

由此可知,InputDisapath线程负责分发,InputDeviceRender线程通过jni方式调用android_server_KeyInputQueue_readEvent(),在这里负责转化C++的按键消息为java的格式,android_server_KeyInputQueue_readEvent在EventHub.cpp中获取按键消息。


具体一些细节代码如下:

WindowManagerService中的KeyQ()类,preporcessEvent函数负责对按键进行预处理,主要的事件类型包括EV_KEY(按键事件)、EV_REL(相对值,如鼠标移动,报告相对于最后一次位置的偏移)和EV_ABS(绝对值,如触摸屏)。

[java] view plain copy print ?
  1. @Override
  2. booleanpreprocessEvent(InputDevicedevice,RawInputEventevent){
  3. if(mPolicy.preprocessInputEventTq(event)){
  4. returntrue;
  5. }
  6. switch(event.type){
  7. caseRawInputEvent.EV_KEY:{
  8. //XXXbeginhack
  9. if(DEBUG){
  10. if(event.keycode==KeyEvent.KEYCODE_G){
  11. if(event.value!=0){
  12. //Gdown
  13. mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
  14. }
  15. returnfalse;
  16. }
  17. if(event.keycode==KeyEvent.KEYCODE_D){
  18. if(event.value!=0){
  19. //dump();
  20. }
  21. returnfalse;
  22. }
  23. }
  24. //XXXendhack
  25. booleanscreenIsOff=!mPowerManager.isScreenOn();
  26. booleanscreenIsDim=!mPowerManager.isScreenBright();
  27. intactions=mPolicy.interceptKeyTq(event,!screenIsOff);/**********按键预处理********//
  28. if((actions&WindowManagerPolicy.ACTION_GO_TO_SLEEP)!=0){
  29. mPowerManager.goToSleep(event.when);
  30. }
  31. if(screenIsOff){
  32. event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
  33. }
  34. if(screenIsDim){
  35. event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
  36. }
  37. if((actions&WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY)!=0){
  38. mPowerManager.userActivity(event.when,false,
  39. LocalPowerManager.BUTTON_EVENT,false);
  40. }
  41. if((actions&WindowManagerPolicy.ACTION_PASS_TO_USER)!=0){
  42. if(event.value!=0&&mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)){
  43. filterQueue(this);
  44. mKeyWaiter.appSwitchComing();
  45. }
  46. returntrue;
  47. }else{
  48. returnfalse;
  49. }
  50. }
  51. caseRawInputEvent.EV_REL:{
  52. booleanscreenIsOff=!mPowerManager.isScreenOn();
  53. booleanscreenIsDim=!mPowerManager.isScreenBright();
  54. if(screenIsOff){
  55. if(!mPolicy.isWakeRelMovementTq(event.deviceId,
  56. device.classes,event)){
  57. //Slog.i(TAG,"droppingbecausescreenIsOffand!isWakeKey");
  58. returnfalse;
  59. }
  60. event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
  61. }
  62. if(screenIsDim){
  63. event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
  64. }
  65. returntrue;
  66. }
  67. caseRawInputEvent.EV_ABS:{
  68. booleanscreenIsOff=!mPowerManager.isScreenOn();
  69. booleanscreenIsDim=!mPowerManager.isScreenBright();
  70. if(screenIsOff){
  71. if(!mPolicy.isWakeAbsMovementTq(event.deviceId,
  72. device.classes,event)){
  73. //Slog.i(TAG,"droppingbecausescreenIsOffand!isWakeKey");
  74. returnfalse;
  75. }
  76. event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
  77. }
  78. if(screenIsDim){
  79. event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
  80. }
  81. returntrue;
  82. }
  83. default:
  84. returntrue;
  85. }
  86. }

preporcessEvent调用了InterceptKeyTQ

PhoneWindowManager.java中的InterceptKeyTQ判断该按键是否应该送给上层,还是在此层进行截取,如待机休眠唤醒则在此层进行截取。

[java] view plain copy print ?
  1. /**{@inheritDoc}*/
[java] view plain copy print ?
  1. //2.3中名为interceptKeyBeforeQueueing
  2. publicintinterceptKeyTq(RawInputEventevent,booleanscreenIsOn){
  3. intresult=ACTION_PASS_TO_USER;
  4. finalbooleanisWakeKey=isWakeKeyTq(event);
  5. //Ifscreenisoffthenwetreatthecasewherethekeyguardisopenbuthidden
  6. //thesameasifitwereopenandinfront.
  7. //Thiswillpreventanykeysotherthanthepowerbuttonfromwakingthescreen
  8. //whenthekeyguardishiddenbyanotheractivity.
  9. finalbooleankeyguardActive=(screenIsOn?
  10. mKeyguardMediator.isShowingAndNotHidden():
  11. mKeyguardMediator.isShowing());
  12. if(false){
  13. Log.d(TAG,"interceptKeyTqevent="+event+"keycode="+event.keycode
  14. +"screenIsOn="+screenIsOn+"keyguardActive="+keyguardActive);
  15. }
  16. if(keyguardActive){
  17. if(screenIsOn){
  18. //whenthescreenison,alwaysgivetheeventtothekeyguard
  19. result|=ACTION_PASS_TO_USER;
  20. }else{
  21. //otherwise,don'tpassittotheuser
  22. result&=~ACTION_PASS_TO_USER;
  23. finalbooleanisKeyDown=
  24. (event.type==RawInputEvent.EV_KEY)&&(event.value!=0);
  25. if(isWakeKey&&isKeyDown){
  26. //tellthemediatoraboutawakekey,itmaydecideto
  27. //turnonthescreendependingonwhetherthekeyis
  28. //appropriate.
  29. if(!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)
  30. &&(event.keycode==KeyEvent.KEYCODE_VOLUME_DOWN
  31. ||event.keycode==KeyEvent.KEYCODE_VOLUME_UP)){
  32. //whenkeyguardisshowingandscreenoff,weneed
  33. //tohandlethevolumekeyforcallsandmusichere
  34. if(isInCall()){
  35. handleVolumeKey(AudioManager.STREAM_VOICE_CALL,event.keycode);
  36. }elseif(isMusicActive()){
  37. handleVolumeKey(AudioManager.STREAM_MUSIC,event.keycode);
  38. }
  39. }
  40. }
  41. }
  42. }elseif(!screenIsOn){
  43. //Ifwearein-callwithscreenoffandkeyguardisnotshowing,
  44. //thenhandlethevolumekeyourselves.
  45. //Thisisnecessarybecausethephoneappwilldisablethekeyguard
  46. //whentheproximitysensorisinuse.
  47. if(isInCall()&&event.type==RawInputEvent.EV_KEY&&
  48. (event.keycode==KeyEvent.KEYCODE_VOLUME_DOWN
  49. ||event.keycode==KeyEvent.KEYCODE_VOLUME_UP)){
  50. result&=~ACTION_PASS_TO_USER;
  51. handleVolumeKey(AudioManager.STREAM_VOICE_CALL,event.keycode);
  52. }
  53. if(isWakeKey){
  54. //awakekeyhasasolepurposeofwakingthedevice;don'tpass
  55. //ittotheuser
  56. result|=ACTION_POKE_USER_ACTIVITY;
  57. result&=~ACTION_PASS_TO_USER;
  58. }
  59. }
  60. inttype=event.type;
  61. intcode=event.keycode;
  62. booleandown=event.value!=0;
  63. if(type==RawInputEvent.EV_KEY){
  64. if(code==KeyEvent.KEYCODE_ENDCALL
  65. ||code==KeyEvent.KEYCODE_POWER){
  66. if(down){
  67. booleanhandled=false;
  68. booleanhungUp=false;
  69. //keyrepeatsaregeneratedbythewindowmanager,andwedon'tseethem
  70. //here,sounlessthedriverisdoingsomethingitshouldn'tbe,weknow
  71. //thisistherealpressevent.
  72. ITelephonyphoneServ=getPhoneInterface();
  73. if(phoneServ!=null){
  74. try{
  75. if(code==KeyEvent.KEYCODE_ENDCALL){
  76. handled=hungUp=phoneServ.endCall();
  77. }elseif(code==KeyEvent.KEYCODE_POWER){
  78. if(phoneServ.isRinging()){
  79. //PressingPowerwhilethere'saringingincoming
  80. //callshouldsilencetheringer.
  81. phoneServ.silenceRinger();
  82. handled=true;
  83. }elseif(phoneServ.isOffhook()&&
  84. ((mIncallPowerBehavior
  85. &Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
  86. !=0)){
  87. //Otherwise,if"Powerbuttonendscall"isenabled,
  88. //thePowerbuttonwillhangupanycurrentactivecall.
  89. handled=hungUp=phoneServ.endCall();
  90. }
  91. }
  92. }catch(RemoteExceptionex){
  93. Log.w(TAG,"ITelephonythrewRemoteException"+ex);
  94. }
  95. }else{
  96. Log.w(TAG,"!!!UnabletofindITelephonyinterface!!!");
  97. }
  98. if(!screenIsOn
  99. ||(handled&&code!=KeyEvent.KEYCODE_POWER)
  100. ||(handled&&hungUp&&code==KeyEvent.KEYCODE_POWER)){
  101. mShouldTurnOffOnKeyUp=false;
  102. }else{
  103. //onlytrytoturnoffthescreenifwedidn'talreadyhangup
  104. mShouldTurnOffOnKeyUp=true;
  105. mHandler.postDelayed(mPowerLongPress,
  106. ViewConfiguration.getGlobalActionKeyTimeout());
  107. result&=~ACTION_PASS_TO_USER;
  108. }
  109. }else{
  110. mHandler.removeCallbacks(mPowerLongPress);
  111. if(mShouldTurnOffOnKeyUp){
  112. mShouldTurnOffOnKeyUp=false;
  113. booleangohome,sleeps;
  114. if(code==KeyEvent.KEYCODE_ENDCALL){
  115. gohome=(mEndcallBehavior
  116. &Settings.System.END_BUTTON_BEHAVIOR_HOME)!=0;
  117. sleeps=(mEndcallBehavior
  118. &Settings.System.END_BUTTON_BEHAVIOR_SLEEP)!=0;
  119. }else{
  120. gohome=false;
  121. sleeps=true;
  122. }
  123. if(keyguardActive
  124. ||(sleeps&&!gohome)
  125. ||(gohome&&!goHome()&&sleeps)){
  126. //theymustalreadybeonthekeyguadorhomescreen,
  127. //gotosleepinstead
  128. Log.d(TAG,"I'mtiredmEndcallBehavior=0x"
  129. +Integer.toHexString(mEndcallBehavior));
  130. result&=~ACTION_POKE_USER_ACTIVITY;
  131. result|=ACTION_GO_TO_SLEEP;
  132. }
  133. result&=~ACTION_PASS_TO_USER;
  134. }
  135. }
  136. }elseif(isMediaKey(code)){
  137. //Thiskeyneedstobehandledevenifthescreenisoff.
  138. //Ifothersneedtobehandledwhileit'soff,thisisareasonable
  139. //patterntofollow.
  140. if((result&ACTION_PASS_TO_USER)==0){
  141. //Onlydothisifwewouldotherwisenotpassittotheuser.Inthat
  142. //case,thePhoneWindowclasswilldothesamething,exceptitwill
  143. //onlydoitiftheshowingappdoesn'tprocessthekeyonitsown.
  144. KeyEventkeyEvent=newKeyEvent(event.when,event.when,
  145. down?KeyEvent.ACTION_DOWN:KeyEvent.ACTION_UP,
  146. code,0);
  147. mBroadcastWakeLock.acquire();
  148. mHandler.post(newPassHeadsetKey(keyEvent));
  149. }
  150. }elseif(code==KeyEvent.KEYCODE_CALL){
  151. //Ifanincomingcallisringing,answerit!
  152. //(Wehandlethiskeyhere,ratherthanintheInCallScreen,tomake
  153. //surewe'llrespondtothekeyeveniftheInCallScreenhasn'tcometo
  154. //theforegroundyet.)
  155. //WeanswerthecallontheDOWNevent,toagreewith
  156. //the"fallback"behaviorintheInCallScreen.
  157. if(down){
  158. try{
  159. ITelephonyphoneServ=getPhoneInterface();
  160. if(phoneServ!=null){
  161. if(phoneServ.isRinging()){
  162. Log.i(TAG,"interceptKeyTq:"
  163. +"CALLkey-downwhileringing:Answerthecall!");
  164. phoneServ.answerRingingCall();
  165. //And*don't*passthiskeythrutothecurrentactivity
  166. //(whichispresumablytheInCallScreen.)
  167. result&=~ACTION_PASS_TO_USER;
  168. }
  169. }else{
  170. Log.w(TAG,"CALLbutton:UnabletofindITelephonyinterface");
  171. }
  172. }catch(RemoteExceptionex){
  173. Log.w(TAG,"CALLbutton:RemoteExceptionfromgetPhoneInterface()",ex);
  174. }
  175. }
  176. }elseif((code==KeyEvent.KEYCODE_VOLUME_UP)
  177. ||(code==KeyEvent.KEYCODE_VOLUME_DOWN)){
  178. //Ifanincomingcallisringing,eitherVOLUMEkeymeans
  179. //"silenceringer".Wehandlethesekeyshere,ratherthan
  180. //intheInCallScreen,tomakesurewe'llrespondtothem
  181. //eveniftheInCallScreenhasn'tcometotheforegroundyet.
  182. //LookfortheDOWNeventhere,toagreewiththe"fallback"
  183. //behaviorintheInCallScreen.
  184. if(down){
  185. try{
  186. ITelephonyphoneServ=getPhoneInterface();
  187. if(phoneServ!=null){
  188. if(phoneServ.isRinging()){
  189. Log.i(TAG,"interceptKeyTq:"
  190. +"VOLUMEkey-downwhileringing:Silenceringer!");
  191. //Silencetheringer.(It'ssafetocallthis
  192. //eveniftheringerhasalreadybeensilenced.)
  193. phoneServ.silenceRinger();
  194. //And*don't*passthiskeythrutothecurrentactivity
  195. //(whichisprobablytheInCallScreen.)
  196. result&=~ACTION_PASS_TO_USER;
  197. }
  198. }else{
  199. Log.w(TAG,"VOLUMEbutton:UnabletofindITelephonyinterface");
  200. }
  201. }catch(RemoteExceptionex){
  202. Log.w(TAG,"VOLUMEbutton:RemoteExceptionfromgetPhoneInterface()",ex);
  203. }
  204. }
  205. }
  206. }
  207. returnresult;
  208. }
WindowManagerService中的InputDispatcherThread线程process,在里头调用mQueue(KeyQ类)的getEvent函数来获取队列中的消息,处理后分发。[java] view plain copy print ?
  1. privatevoidprocess(){
  2. android.os.Process.setThreadPriority(
  3. android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
  4. //Thelastkeyeventwesaw
  5. KeyEventlastKey=null;
  6. //Lastkeydowntimeforauto-repeatingkeys
  7. longlastKeyTime=SystemClock.uptimeMillis();
  8. longnextKeyTime=lastKeyTime+LONG_WAIT;
  9. longdownTime=0;
  10. //Howmanysuccessiverepeatswegenerated
  11. intkeyRepeatCount=0;
  12. //Needtoreportthatconfigurationhaschanged?
  13. booleanconfigChanged=false;
  14. while(true){
  15. longcurTime=SystemClock.uptimeMillis();
  16. if(DEBUG_INPUT)Slog.v(
  17. TAG,"Waitingfornextkey:now="+curTime
  18. +",repeat@"+nextKeyTime);
  19. //Retrievenextevent,waitingonlyaslongasthenext
  20. //repeattimeout.Iftheconfigurationhaschanged,then
  21. //don'twaitatall--we'llreportthechangeassoonas
  22. //wehaveprocessedallevents.
  23. QueuedEventev=mQueue.getEvent(//*****获取队列中的消息***//
  24. (int)((!configChanged&&curTime<nextKeyTime)
  25. ?(nextKeyTime-curTime):0));
  26. if(DEBUG_INPUT&&ev!=null)Slog.v(
  27. TAG,"Event:type="+ev.classType+"data="+ev.event);
  28. if(MEASURE_LATENCY){
  29. lt.sample("2gotevent",System.nanoTime()-ev.whenNano);
  30. }
  31. if(lastKey!=null&&!mPolicy.allowKeyRepeat()){
  32. //cancelkeyrepeatattherequestofthepolicy.
  33. lastKey=null;
  34. downTime=0;
  35. lastKeyTime=curTime;
  36. nextKeyTime=curTime+LONG_WAIT;
  37. }
  38. try{
  39. if(ev!=null){
  40. curTime=SystemClock.uptimeMillis();
  41. inteventType;
  42. if(ev.classType==RawInputEvent.CLASS_TOUCHSCREEN){
  43. eventType=eventType((MotionEvent)ev.event);
  44. }elseif(ev.classType==RawInputEvent.CLASS_KEYBOARD||
  45. ev.classType==RawInputEvent.CLASS_TRACKBALL){
  46. eventType=LocalPowerManager.BUTTON_EVENT;
  47. }else{
  48. eventType=LocalPowerManager.OTHER_EVENT;
  49. }
  50. try{
  51. if((curTime-mLastBatteryStatsCallTime)
  52. >=MIN_TIME_BETWEEN_USERACTIVITIES){
  53. mLastBatteryStatsCallTime=curTime;
  54. mBatteryStats.noteInputEvent();
  55. }
  56. }catch(RemoteExceptione){
  57. //Ignore
  58. }
  59. if(ev.classType==RawInputEvent.CLASS_CONFIGURATION_CHANGED){
  60. //donotwakescreeninthiscase
  61. }elseif(eventType!=TOUCH_EVENT
  62. &&eventType!=LONG_TOUCH_EVENT
  63. &&eventType!=CHEEK_EVENT){
  64. mPowerManager.userActivity(curTime,false,
  65. eventType,false);
  66. }elseif(mLastTouchEventType!=eventType
  67. ||(curTime-mLastUserActivityCallTime)
  68. >=MIN_TIME_BETWEEN_USERACTIVITIES){
  69. mLastUserActivityCallTime=curTime;
  70. mLastTouchEventType=eventType;
  71. mPowerManager.userActivity(curTime,false,
  72. eventType,false);
  73. }
  74. switch(ev.classType){
  75. caseRawInputEvent.CLASS_KEYBOARD:
  76. KeyEventke=(KeyEvent)ev.event;
  77. if(ke.isDown()){
  78. lastKey=ke;
  79. downTime=curTime;
  80. keyRepeatCount=0;
  81. lastKeyTime=curTime;
  82. nextKeyTime=lastKeyTime
  83. +ViewConfiguration.getLongPressTimeout();
  84. if(DEBUG_INPUT)Slog.v(
  85. TAG,"Receivedkeydown:firstrepeat@"
  86. +nextKeyTime);
  87. }else{
  88. lastKey=null;
  89. downTime=0;
  90. //Arbitrarylongtimeout.
  91. lastKeyTime=curTime;
  92. nextKeyTime=curTime+LONG_WAIT;
  93. if(DEBUG_INPUT)Slog.v(
  94. TAG,"Receivedkeyup:ignorerepeat@"
  95. +nextKeyTime);
  96. }
  97. dispatchKey((KeyEvent)ev.event,0,0);
  98. mQueue.recycleEvent(ev);
  99. break;
  100. caseRawInputEvent.CLASS_TOUCHSCREEN:
  101. //Slog.i(TAG,"Readnextevent"+ev);
  102. dispatchPointer(ev,(MotionEvent)ev.event,0,0);
  103. break;
  104. caseRawInputEvent.CLASS_TRACKBALL:
  105. dispatchTrackball(ev,(MotionEvent)ev.event,0,0);
  106. break;
  107. caseRawInputEvent.CLASS_CONFIGURATION_CHANGED:
  108. configChanged=true;
  109. break;
  110. default:
  111. mQueue.recycleEvent(ev);
  112. break;
  113. }
  114. }elseif(configChanged){
  115. configChanged=false;
  116. sendNewConfiguration();
  117. }elseif(lastKey!=null){
  118. curTime=SystemClock.uptimeMillis();
  119. //Timeoutoccurredwhilekeywasdown.Ifitisator
  120. //pastthekeyrepeattime,dispatchtherepeat.
  121. if(DEBUG_INPUT)Slog.v(
  122. TAG,"Keytimeout:repeat="+nextKeyTime
  123. +",now="+curTime);
  124. if(curTime<nextKeyTime){
  125. continue;
  126. }
  127. lastKeyTime=nextKeyTime;
  128. nextKeyTime=nextKeyTime+KEY_REPEAT_DELAY;
  129. keyRepeatCount++;
  130. if(DEBUG_INPUT)Slog.v(
  131. TAG,"Keyrepeat:count="+keyRepeatCount
  132. +",next@"+nextKeyTime);
  133. KeyEventnewEvent;
  134. if(downTime!=0&&(downTime
  135. +ViewConfiguration.getLongPressTimeout())
  136. <=curTime){
  137. newEvent=KeyEvent.changeTimeRepeat(lastKey,
  138. curTime,keyRepeatCount,
  139. lastKey.getFlags()|KeyEvent.FLAG_LONG_PRESS);
  140. downTime=0;
  141. }else{
  142. newEvent=KeyEvent.changeTimeRepeat(lastKey,
  143. curTime,keyRepeatCount);
  144. }
  145. dispatchKey(newEvent,0,0);
  146. }else{
  147. curTime=SystemClock.uptimeMillis();
  148. lastKeyTime=curTime;
  149. nextKeyTime=curTime+LONG_WAIT;
  150. }
  151. }catch(Exceptione){
  152. Slog.e(TAG,
  153. "Inputthreadreceiveduncaughtexception:"+e,e);
  154. }
  155. }
  156. }
  157. }

个人水平有限,有错误欢迎指出,谢谢。


补充:

1、生成

存在这样一个线程,它不断地从driver读取Event,并把它放到RawEvent队列中。这个队列中的RawEvent既有按键,也有触摸、轨迹球等事件。

RawEvent队列中的每个RawEvent最后都会通过一系列转化,最终变为KeyEvent被发送给另外一个线程,即输入线程,也就是一个Activity的主线程。

2、传递

KeyEvent传递过程主要可以划分为三步:过滤器、View树、Activity

过滤器部分主要对应着PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。它们的代码可以在frameworks/base/policy/base/phone/com/Android/internal/policy/impl/PhoneWindowManager.java中看到。

这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。

在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器,如果这个过滤器不吃掉的话,就会继续前进,进入View树,如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。

当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。


更多相关文章

  1. android异步图片加载三之handler+线程池+消息队列模式
  2. Android进程与线程基本知识一
  3. 有关Android线程的学习
  4. Android单线程模型相关概念详解
  5. Android 多线程-----AsyncTask详解
  6. 浅析Android线程模型
  7. android > 调用拨打电话 并子线程监控然后返回跳转
  8. android按键模拟测试

随机推荐

  1. Android Studio Check for Update
  2. Android Tablet PC avec Android 4.1 Jel
  3. android中Handler的源码分析
  4. Android 各种布局技术-五大布局对象
  5. Android数据的四种存储方式SharedPrefere
  6. Android Camera (android2.2) 资料一
  7. android 关于tts的一些参数
  8. android原生项目整合ReactNative
  9. 搭建android测试环境
  10. android sdk setup时出现:HTTPS SSL erro