面试题剖析:android全局触摸事件的前世与今生InputMonitor/SpyWindow
背景:
昨天整理分享了惊奇学员们零零散散给我发过的一些面试题目:
安卓系统fw工程师面试福利9:近期收集学员手机车载FW面试真题
有vip学员朋友在群里对12题进行了讨论:
他的回答如下:
这个同学回答其实还是比较认真的,他认为因为以前的Monitor是只有systemserver进程才可以进行监听的,所以要系统app可以监听全局触摸必须要sypwindow才可以,其实这个回答还是不那么准确哈。
这个面试题目如果要想回答好,其实还是需要对全局触摸事件监听的历史发展版本有一个深入的了解才可以,本文将针对触摸事件的全局监听这块前世与今生进行剖析。
低版本安卓10-12全局InputMonitor方式(前世)
android 10版本的全局事件监听实现:
frameworks/base/core/java/android/hardware/input/InputManager.java
/*** Monitor input on the specified display for gestures.** @hide*/public InputMonitor monitorGestureInput(String name, int displayId) {try {return mIm.monitorGestureInput(name, displayId);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
/*** Creates an input monitor that will receive pointer events for the purposes of system-wide* gesture interpretation.** @param inputChannelName The input channel name.* @param displayId Target display id.* @return The input channel.*/@Override // Binder callpublic InputMonitor monitorGestureInput(String inputChannelName, int displayId) {if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,"monitorInputRegion()")) {throw new SecurityException("Requires MONITOR_INPUT permission");}Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");if (displayId < Display.DEFAULT_DISPLAY) {throw new IllegalArgumentException("displayId must >= 0.");}final long ident = Binder.clearCallingIdentity();try {InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);InputMonitorHost host = new InputMonitorHost(inputChannels[0]);inputChannels[0].setToken(host.asBinder());nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,true /*isGestureMonitor*/);return new InputMonitor(inputChannelName, inputChannels[1], host);} finally {Binder.restoreCallingIdentity(ident);}}
注册后使用dumpsys input可以展示如下:


高版本安卓13及以上更新成了SpyWindow方式 (今生)
android 13版本的全局事件监听实现:
frameworks/base/core/java/android/hardware/input/InputManager.java
/*** Monitor input on the specified display for gestures.** @hide*/public InputMonitor monitorGestureInput(String name, int displayId) {try {return mIm.monitorGestureInput(new Binder(), name, displayId);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}
服务端system_server实现
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
/*** Creates an input monitor that will receive pointer events for the purposes of system-wide* gesture interpretation.** @param requestedName The input channel name.* @param displayId Target display id.* @return The input channel.*/@Override // Binder callpublic InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,int displayId) {final long ident = Binder.clearCallingIdentity();try {final InputChannel inputChannel =createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid);return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));} finally {Binder.restoreCallingIdentity(ident);}}
使用dumpsys input看看输出情况:

剖析SpyWindow是个啥?
解释这个还是使用官方的注释比较全面
frameworks/base/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java

/*** An internal implementation of an {@link InputMonitor} that uses a spy window.** This spy window is a layer container in the SurfaceFlinger hierarchy that does not have any* graphical buffer, but can still receive input. It is parented to the DisplayContent so* that it can spy on any pointer events that start in the DisplayContent bounds. When the* object is constructed, it will add itself to SurfaceFlinger.*/
上面注释简总结如下:
1、 spy window是一个SurfaceFlinger中的layer,但是layer没有任何图像内容显示
2、但是这个layer是可以监听当前displaycontent所有输入事件
为什么用SpyWindow替代InputMonitor思考?
以下属于个人观点:
先看看二者差异及共同点
共同点:
二者都可以实现对全局事件的监听,即display上的所有事件都可以监听到
差异:
InputMonitor其实是直接在InputDispatch中有InputChannel,直接InputDispatcher的所有输入事件发送到这个InputChannel,没有任何的InputWindow的相关信息,完全是单独属于global monitor处理
Spy Window属于和其他普通的InputWindow没啥区别,InputWindow类型是SPY,InputDispatcher在派发窗口收集时候识别SPY后会进行优先放入。
为啥要升级为SpyWindow抛弃原来的InputMonitor方式
因为这块没有找到google官方的一个说明,所以下面的都是个人观点(大家有其他观点也可以留言讨论哈):
遵守统一
Spy Window 完全遵守现有的窗口管理和输入分发规则,InputDispatcher 以完全相同的方式处理它和普通应用窗口。
Monitor 的一些痛点问题
Monitor 可能无法完全感知到窗口的瞬时状态如窗口关闭,那么还需要注册InputMonitor一定记得注销,不然可能导致错误的拦截决策,但是变成窗口了那就统一不需要额外关注。
原文参考:
https://mp.weixin.qq.com/s/Dp0GvB50qowA1OpuO2tfFA
更多framework实战开发干货,请关注下面“千里马学框架”
