背景:
前几天也有vip学员朋友在群里问道了关于系统Key拦截部分的问题,大概就是在 Android 输入事件处理流程中,interceptKeyBeforeQueueing 和 interceptKeyBeforeDispatching 是两个关键的系统级拦截方法,那么同样为系统级别拦截事件方法,请问他们有什么差异呢?为什么系统中要搞两个拦截呢?具体自己编写相关拦截业务时候应该如何考虑在哪个方法进行拦截呢?
针对上面疑问本节会对系统中拦截事件两个方法进行详细对比
1. 触发阶段
方法名
interceptKeyBeforeQueueing
触发阶段
事件入队前
(系统将按键事件放入全局队列前)
方法名
interceptKeyBeforeDispatching
触发阶段
事件分发前
(系统准备将事件分发给目标窗口前)
3. 典型应用场景
interceptKeyBeforeQueueing - 电源键亮屏/灭屏
- 音量键调节全局音量
- 截屏快捷键(如 Power + Vol-)
interceptKeyBeforeDispatching
- HOEM按键
- 应用内自定义快捷键(如 Ctrl+S 保存)
4. 代码逻辑对比
interceptKeyBeforeQueueing部分:
方法原型
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)
可以看出比较简单,就一个event,还有一个policyFlags
代码比较多,这里以POWER电源按键为例进行展示代码分析:
上面代码就看出了,只要是POWER按键,result就会排除掉ACTION_PASS_TO_USER这个值,返回InputDispatcher后就靠这个值来确定是否拦截,一般有ACTION_PASS_TO_USER这个值就代表不拦截。
interceptKeyBeforeDispatching 部分:
方法原型:
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags)
明显可以看出这里多了一个窗口相关focusedToken参数,主要就是拦截相关逻辑需要结合窗口。
这里以HOME按键为例进行展示代码分析:
interceptKeyBeforeDispatching的方法主要会调用到interceptSystemKeysAndShortcuts
下面看看interceptSystemKeysAndShortcuts方法
下面来来看看handleHomeShortcuts方法
private boolean handleHomeShortcuts(IBinder focusedToken, KeyEvent event) {
//省略
return handler.handleHomeButton(focusedToken, event);
}
上面方法主要又是调用到handler.handleHomeButton方法
boolean handleHomeButton(IBinder focusedToken, KeyEvent event) {
//省略部分
//通过focusedToken获取相关窗口信息
final KeyInterceptionInfo info =
mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
if (info != null) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
//判断是否窗口类型为锁屏相关,如果是就不拦截事件
if (info.layoutParamsType == TYPE_KEYGUARD_DIALOG
|| (info.layoutParamsType == TYPE_NOTIFICATION_SHADE
&& isKeyguardShowing())) {
// the "app" is keyguard, so give it the key
return false;
}
for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
if (info.layoutParamsType == t) {
// don't do anything, but also don't pass it to the app
return true;
}
}
}
//省略部分
handleHomeButton方法中就有核心逻辑,会有不拦截HOME按键的场景,具体会有如下代码逻辑:
1、通过focusedToken获取到对应的window相关信息
2、判断窗口类型是不是锁屏相关的,如果是锁屏相关窗口,切显示者,则直接不拦截
总结
interceptKeyBeforeQueueing:
最开始的事件拦截,也就是拦截的优先级最高,系统级“紧急开关”,确保全局按键(如电源键)立即生效,无需等待应用响应,事件也压根不会传递给应用。
interceptKeyBeforeDispatching:
如果interceptKeyBeforeQueueing方法不拦截该事件,那么就开始准备派发给应用,会先获取当前的FocusWindow,在派发应用前进行事件相关事件拦截。应用级“筛选器”,根据当前窗口状态决定是否将事件传递给应用,比如HOME按键按下时候,就会考虑当前是否为锁屏画面。
两者的协同工作,保证了 Android 系统既能快速响应关键操作,又能灵活处理应用层交互。
更多framework实战干货,请关注下面“千里马学框架”