在Android系统中,键盘按键事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序处理,不过和普通消息不一样,它是由硬件中断触发的;本文将结合这种消息处理机制来详细分析Android应用程序是如何获得键盘按键消息的。
在系统启动的时候,SystemServer会启动窗口管理服务WindowManagerService,WindowManagerService 在启动的时候就会通过系统输入管理器InputManager来总负责监控键盘消息。这些键盘消息一般都是分发给当前激活的Activity窗口来处理的,因此,当前激活的Activity窗口在创建的时候,会到WindowManagerService中去注册一个接收键盘消息的通道,表明它要处理键盘消息,而当InputManager监控到有键盘消息时,就会分给给它处理。当当前激活的Activity窗口不再处于激活状态时,它也会到 WindowManagerService中去反注册之前的键盘消息接收通道,这样,InputManager就不会再把键盘消息分发给它来处理。
WindowManagerService中的处理函数interceptKeyBeforeDispatching和interceptKeyBeforeQueueing是在PhoneWindowManager中实现的。
Android4.x在Framework的PhoneWindowManager在监听按键事件的时候对Power(KeyEvent.KEYCODE_POWER)和Home(KeyEvent.KEYCODE_HOME)键做了处理,不会把这些键传送上层应用程序。如需要把这些键发送给Activity和Service,需要在PhoneWindowManager处理这些键时“发送一个广播出去,然后在应用程序接收到广播后做处理”。
Home键在KeyEvent中的键值为3.即:KEYCODE_HOME = 3.
当用户按下home键的时候(包括长按),程序会进入到PhoneWindowManager中的interceptKeyBeforeDispatching这个方法中进行处理。如果用户是连续点击Home,此时就要执行长按Home事件了。即执行mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());对应的代码。也就会跳转到mHomeLongPress这个Runnable接着往下执行。
interceptKeyBeforeDispatching这个方法位于PhoneWindowManager.java中。
位置为:/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
final boolean down = (action == KeyEvent.ACTION_DOWN);
...
//4、用户按下home,然后马上释放。此时这个条件成立。将之前postDelayed的事件remove掉。此时就不会执行长按home事件。
if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) {
mHandler.removeCallbacks(mHomeLongPress);
}
//5、第一次按下home,mHomePressed为false。
if (mHomePressed) {
if (keyCode == KeyEvent.KEYCODE_HOME) {
//a、如果用户连续按下home,此时暂时没有up事件。所以就不走这里。
//b、如果用户没有连续按下home,此时过来的是up(move或者...)事件。即!down为true,执行该方法
if (!down) {
mHomePressed = false;
if (!canceled) {
boolean incomingRinging = false;
try {
ITelephony telephonyService = getTelephonyService();
if (telephonyService != null) {
incomingRinging = telephonyService.isRinging();
}
} catch (RemoteException ex) {
Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
}
if (incomingRinging) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
} else {
//单击home处理
launchHomeFromHotKey();
}
} else {
Log.i(TAG, "Ignoring HOME; event canceled.");
}
}
}
return true;
}
...
// 1、第一次处理home按下
if (keyCode == KeyEvent.KEYCODE_HOME) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applicati