漏学Input知识系列之“偷”走了其他窗口的事件pilferPointers

Android10 SystemUI系统手势导航 从Android9开始,android系统就添加了系统导航手势,对于现在高版本的手机,有些系统默认就是开启导航手势的,有些还是沿用以前导航栏,但是也是可以在设置中开启导航手势,导航栏会自动消失,这篇文章主要讲解的是系统导航收拾是如何实现的。 对于系统导航手势的实现,android10还是在SystemUI中实现的,在以后的版本中可能就是在Launcher3中,SystemUI是一个系统应用,在SystemServer中启动,先来看下是如何启动的: ... 阅读详情
AI摘要

本文围绕Android系统中触摸事件相关问题展开,介绍了pilferPointers方法“偷”走其他窗口事件的原理。当导航手势识别到上滑动作,会独占上滑事件,系统给Activity发送CANCEL事件。还讲解了通过日志查看“偷”事件的方式,以及pilferPointers的使用和源码分析。

背景:

学员群里近来老是学员朋友问到一个触摸事件相关实际场景问题:
如何在全局监听到某个类型触摸事件比如NavigationBar导航手势的上滑动,不会让这个上滑动事件不影响app。

简单说就是触摸事件正常情况会传递给导航手势和顶部Activity,但是很多情况下导航手势都对Activity的触摸事件不感兴趣,但是一旦是Activity底部开始上滑这个动作,那么导航手势就感兴趣,他要开始处理整体的上滑逻辑了,自然这个上滑事件再传递给Activity就不合适了,那么系统是怎么做到的不再传递给Activity呢?

这个就是本文带大家了解的pilferPointers方法,“偷”走了其他窗口的事件。这里用了一个表达很到位的英文单词pilfer即“偷”。为啥叫做“偷”不叫“拦截”,因为拦截事件一般是指上层策略压根事件不让给你,“偷”事件则表达事件之前可能是传递给你的,本来也一直要传递给你,但是因为导航手势业务把给你事件都偷走了。

具体如下图所示:在这里插入图片描述

直观理解“偷”事件

下面以App的Activity角度的触摸事件情况来理解这个“偷”事件
到Activity的dispatchTouchEvent方法中加入相关的打印查看:
在这里插入图片描述正常Activity非底部上滑

在这里插入图片描述
Activity底部上滑触发导航手势
导航手势上滑
也就是直观上其实可以看出这个pilferPointers方法是怎么“偷”事件的。原理如下:
在导航手势还没有识别到自己上滑动作前,其实所有的触摸事件是已经正常传递给了Activity,但是一旦导航手势识别了自己有上滑动作,那么就会独占这个上滑事件,不给Activity,但是前面的DOWN事件已经传递给了Activity,为了事件完整性就需要系统给一个CANCEL事件给Activity。这里是不是额知道了一个CANCEL事件产生的场景。

通过其他日志查看“偷”事件方式:
通过查看events日志
adb logcat -b events | grep cancel

test@test:~/aosp15$ adb logcat -b events -c;adb logcat -b events | grep cancel
01-10 10:44:53.298   546  2344 I input_cancel: [59551b2 com.example.anrdemo/com.example.anrdemo.MainActivity (server),reason=input channel stole pointer stream]
01-10 10:44:53.298   546  2344 I input_cancel: [[Gesture Monitor] edge-swipe (server),reason=input channel stole pointer stream]
01-10 10:44:53.298   546  2344 I input_cancel: [[Gesture Monitor] onehanded-touch (server),reason=input channel stole pointer stream]

可以看到上面就有被偷每个窗口input_cancel这样事件产生,产生原因是input channel stole pointer stream。

也可以使用如下命令过滤stealing关键字

adb logcat | grep stealing
可以看到如下日志输出,这里看到了

01-10 10:49:03.629   546  2344 I InputDispatcher: Channel [Gesture Monitor] swipe-up (server) is stealing input gesture for device 12 from [512067 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher, dca494a com.android.systemui.wallpapers.ImageWallpaper, [Gesture Monitor] edge-swipe, [Gesture Monitor] onehanded-touch]

这个日志就明显展示了,事件被 swipe-up这个Monitor独占了,把事件从其他window中偷走了。

使用pilferPointers及源码分析
以导航手势上滑为例,剖析一下使用

相关的pilferPointers调用堆栈如下

pilferPointers:59, InputMonitor (android.view)
pilferPointers:55, InputMonitorCompat (com.android.systemui.shared.system)
notifyGestureStarted:382, OtherActivityInputConsumer (com.android.quickstep.inputconsumers)
onMotionEvent:339, OtherActivityInputConsumer (com.android.quickstep.inputconsumers)
onMotionEvent:116, NavHandleLongPressInputConsumer (com.android.quickstep.inputconsumers)
onMotionEvent:138, OneHandedModeInputConsumer (com.android.quickstep.inputconsumers)
onInputEvent:937, TouchInteractionService (com.android.quickstep)
$r8$lambda$JDK2db7a_ZtzI6D1EJ5hYukunXo:-1, TouchInteractionService (com.android.quickstep)
onInputEvent:0, TouchInteractionService$$ExternalSyntheticLambda5 (com.android.quickstep)
onInputEvent:78, InputChannelCompat$InputEventReceiver$1 (com.android.systemui.shared.system)
dispatchInputEvent:295, InputEventReceiver (android.view)
nativeConsumeBatchedInputEvents:-1, InputEventReceiver (android.view)
consumeBatchedInputEvents:259, InputEventReceiver (android.view)
onBatchedInputEventPending:58, BatchedInputEventReceiver (android.view)
nativePollOnce:-1, MessageQueue (android.os)
next:346, MessageQueue (android.os)
loopOnce:189, Looper (android.os)
loop:317, Looper (android.os)
main:8705, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:580, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:886, ZygoteInit (com.android.internal.os)

这个全局的monitor就是swipe-up即导航手势
InputMonitor { inputChannel = [Gesture Monitor] swipe-up (client), host = android.view.IInputMonitorHost S t u b Stub StubProxy@f9a7d10, surface = Surface(name=[Gesture Monitor] swipe-up)/@0x1425009 }

对应的代码如下:
在满足对应上滑条件后会调用notifyGestureStarted方法
在这里插入图片描述在notifyGestureStarted方法中主要是调用到了pilferPointers方法
在这里插入图片描述
这里使用其实很简单只需要在识别到满足条件的触摸事件后调用pilferPointers方法既可以。

下面重点分析一下pilferPointers的源码
frameworks/base/core/java/android/view/InputMonitor.java

    public void pilferPointers() {
        try {
            mHost.pilferPointers();
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

这里主要掉到了 mHost的pilferPointers,这里的mHost是IInputMonitorHost类型,属于一个跨进程通讯,是在InputMonitor构造时候进行赋值的

在这里插入图片描述

这里的InputMonitor又是在InputManagerService中调用monitorGestureInput时候构造的

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java在这里插入图片描述
所以上面 mHost.pilferPointers()调用就是调用到了InputManagerService中的InputMonitorHost方法:
在这里插入图片描述
这里又会传递一个自己的mInputChannelToken,调用到Native层面,最后通过如下方法调用到了InputDispatcher中
在这里插入图片描述

具体InputDispatcher中的pilferPointers方法如下:

status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
    std::scoped_lock _l(mLock);
    return pilferPointersLocked(token);
}

status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
    const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token);
    //根据上层传递token,找到对应的window
    auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
  

    ScopedSyntheticEventTracer traceContext(mTracer);
    for (const DeviceId deviceId : deviceIds) {
        TouchState& state = *statePtr;
        TouchedWindow& window = *windowPtr;
        // Send cancel events to all the input channels we're stealing from.
        //准备好cancel事件
        CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                   "input channel stole pointer stream", traceContext.getTracker());
        options.deviceId = deviceId;
        options.displayId = displayId;
        std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId);
        std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers);
        options.pointerIds = pointerIds;

        std::string canceledWindows;
        for (const TouchedWindow& w : state.windows) {
        		 //除了token对应window,其他window都要收到cancel
            if (w.windowHandle->getToken() != token) {
                synthesizeCancelationEventsForWindowLocked(w.windowHandle, options);
                canceledWindows += canceledWindows.empty() ? "[" : ", ";
                canceledWindows += w.windowHandle->getName();
            }
        }
        canceledWindows += canceledWindows.empty() ? "[]" : "]";
        //这个就是打印
        LOG(INFO) << "Channel " << requestingConnection->getInputChannelName()
                  << " is stealing input gesture for device " << deviceId << " from "
                  << canceledWindows;

        // Prevent the gesture from being sent to any other windows.
        // This only blocks relevant pointers to be sent to other windows
        window.addPilferingPointers(deviceId, pointerIds);
			//对state中保存的接受事件window要进行移除掉,只剩下token对应window,那样事件就不会派发给cancelwindow
        state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
    }
    return OK;
}

总结

“偷”走了其他窗口的事件,本质实现就是调用pilferPointers方法,实现让其他窗口收到CANCEL事件,同时派发的窗口列表中移除非token的window,后续事件派发时候,只派发给token对应的window。

文章来源:https://mp.weixin.qq.com/s/l_mQK1xs8d4dp7VMkl0kyw

更多framework实战干货,请关注下面“千里马学框架”

android手势分析(应用界面左往右边滑动退出应用) 其中,move状态下的 handleMoveEvent()是主要的处理逻辑:判断 x 轴的 offset 数值是否达到了阈值 mSwipeThreshold,从而 回调 BackCallback 事件 和当前视图的更新,可看出 NavigationBarEdgePanel 就是一个 自定义view,根据 控制器 传递过来的 MotionEvent 实现具体的UI 效果,并回传事件。备注:在PIXEL手机手势设置页面,关闭gesture navigation会回调。 阅读详情

相关推荐

Android 手势导航(从下往上滑动进入多任务页面)

onInputEvent 函数中处理滑动事件,在 DOWN 事件时根据不同的场景创建不同的 InputConsumer,例如在桌面、或其他界面等不同情况下使用手势,对应的 InputConsumer 是不同的,最常见的就是 OtherActivityInputConsumer (其他Activity界面使用手势导航)。判断最终是执行的 HOMO 还是 RECENTS 等事件是在 mInteractionHandler (BaseSwipeUpHandlerV2.java) 中根据滑动中的数据具体判断。

allen_xu_2012_new的博客 4171

Android SystemUI的一些知识点的记录

1.各模块启动时机 2. android:directBootAware="true"的作用 3.Android10手势导航分析

qq_23129309的博客 2187

Android 全局手势识别原理

本文是对全局手势识别进行分析,那什么是全局手势呢?简单来说就是在任何界面都需要识别的手势,比如:在任何界面从手机屏幕左侧滑动,当前的界面会退出 (类似 back 键)。我们知道,在 Android 系统中一个 Activity 在显示时,当对屏幕触摸事件进行响应时,经过了许多逻辑处理,详细分析可以参考之前对 IMS 原理分析的一系列文章:中的输入系统章节。接下来对全局手势事件注册监听及处理进行分析。目前使用10.0代码分析。

liuwg1226的专栏 2615

Android 注册屏幕触摸事件拦截事件分发

接到需求,需要实现屏幕双指上下滑动触发对应功能,并且拦截滑动冲突;目前世面上能看到很多类型的功能,比如侧滑返回、三指下滑截屏等等;本次基于Android10,且应用为系统应用!在原生代码暂未发现关于三指滑动的源码,但是好在有侧滑返回源码,在机器上使用可以看到是有处理滑动冲突的。本文基于Android10源码。

一副皮囊的博客 732

Android 手势导航(Launcher3 部分)

手势导航功能的实现主要由 SystemUI + Launcher3 共同处理,在 Launcher3 中启动一个Services Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java

tq501501的博客 7542

基于CODESYS与EtherCAT的步进电机单轴控制实践

本文详细介绍了基于CODESYS平台与EtherCAT总线技术实现步进电机单轴控制的完整实践流程。从硬件选型、软件工程创建、EtherCAT主从站配置,到运动控制功能块编程与在线调试,手把手指导读者完成从零到一的搭建,并提供了常见问题的排坑指南,帮助自动化开发者快速掌握这一高效、灵活且成本可控的工业运动控制解决方案。

hh234的博客 491

Android 返回手势详解

本文基于 AOSP 源码,梳理 SystemUI 的如何通过 InputDispatcher 的 Spy Window / Global Monitor 机制监听屏幕边缘返回手势,并分析事件分发、窗口注册、指针窃取的完整链路。

congqingbin的专栏 455

[官方培训]07-UE材质基础 孙丹璐 Epic 笔记

勾选允许自定义深度写入再在模型上勾选然后可以通过Scene Texture节点拿到渲染的自定义深度信息,有了自定义深度信息就可以与当前渲染的像素深度进行对比,自定义深度信息存放的是最靠近我们的深度,如果当前像素离自定义深度比较远我们就不要这个像素。效果如下这种方案的缺点也很明显,因为这样看不到第二层的透明了,如果是两层的半透明的裙摆,这样就看不到第二层的裙摆了。对于半透明排序问题最根本能解决的还是光线追踪,ue5中又加入了OIT(基于像素的排序)。官方透明材质的理解和应用。

Tcohneyn的博客 1562

Android 10 手势导航源码分析

Android O之前的虚拟按键,基本的控制方法都是在SystemUI中做处理的,在Android 10上为了在手势导航操作时其动画更加流畅,与Launcher互动效果更好,google的设计师就把手势导航相关的操作放到了Launcher3中,而且为了与SystemUI进行信息同步,利用两个aidl的文件利用binder做Launcher3与Systemui之前的进程通信。 在分析之前我们...

qq_26825819的博客 1万+

【STM32】STM32 CANFD外设的几种消息接收过滤模式

在控制器区域网络(CAN)系统中,正确配置消息过滤器对于有效管理网络通信至关重要。特别是在使用Flexible Data-rate Controller Area Network(FDCAN)时,选择合适的过滤器类型可以大大提高网络效率和消息处理速度。

林愿留的博客 6226

CloudFlare CDN优化延迟

这两天在倒腾一个小网站,因为刚注册的域名还没来得及备案,所以就解析到了国外的服务器临时撸代码。为了防止不必要的麻烦,所以在前端套了CloudFlare,但是延迟就比较感人了,通常在150+左右还时不时的抽风,所以就找了点资料来优化一下这个延迟问题。然后再新增一些CloudFlare的亚太地区节点ip的A解析,有耐心的可以分为电信线路和联通线路,我比较懒,所以只分了境内和默认。众所周知CloudFlare是智齿cname解析的,那么我们今天就要用到这个办法了,这里要注意一下,默认的解析是不能动的。

Zuozi - 网络从业者长见识就来Zuozi.NET 1084

android11 input 事件 1 初始化

evenhub 通过epool_create 、epoll_add、 epoll_wait 监测/dev/input/eventX ,是否有事件。listener 实际上就是InputDispatcher,调用InputDispatcher::notifyKey 传递事件。com_android_server_input_InputManagerService.cpp 是jni 实现。将event组合成了一个notifykeyArgs数据结构,同时调用了listener的notifykey方法。

u012794472的专栏 725

告别反复格式化!用Ventoy制作2025年终极启动盘,Windows/Linux/macOS一网打尽

本文详细介绍了如何使用Ventoy制作2025年终极多系统启动盘,彻底告别反复格式化U盘的烦恼。Ventoy通过创新的引导加载器架构,允许用户在一个U盘内同时存放并直接启动Windows、Linux、macOS等多种系统镜像和WePE等维护工具,实现真正的非破坏性管理和极高的U盘利用率。文章涵盖从安装配置、镜像库构建到安全启动设置等全流程实战指南。

weixin_29280219的博客 242

揭秘Android系统返回手势的实现原理

/ 今日科技快讯 /近日,中国信息通信研究院推出了手机号“一键查询”和“一键解绑”服务,可以查询并解除本人手机账号或前手机账号主人所绑定的互联网关联账号。尽管很快有工作人员向媒体回应称,该功能尚处于内测阶段,还没有正式对外发布,但网友对此呼声极高。/ 作者简介 /明天就是周五啦,提前祝大家周末愉快!本篇转自TechMerger的博客,文章主要分享了系统返回...

郭霖 2176

STM32F4三重ADC+DMA双缓冲实战:用CubeMX搞定同步采样,告别数据覆盖烦恼

本文详细介绍了STM32F4三重ADC与DMA双缓冲技术的实战应用,通过CubeMX配置实现同步采样,解决数据覆盖问题。内容涵盖硬件架构解析、CubeMX工程配置、双缓冲实现技巧及调试优化方法,适用于电机控制、电力监测等需要高精度多通道信号采集的场景。

weixin_30627341的博客 357

基于Stateflow的驱动电机控制系统模式管理与安全策略研究

摘要 本文基于Stateflow状态机建模工具,研究了驱动电机控制系统的模式管理架构与安全策略实现方法。通过分析电机控制系统的工作模式,设计了包含初始化、待机、校准、对齐、运行、故障、复位七种状态的有限状态机模型,并建立了三级故障处理机制和降额保护策略。基于Simulink/Stateflow搭建的仿真模型实现了模式切换逻辑和安全管理功能,验证了系统能够正确响应各类控制事件,故障处理机制可有效保护系统安全。研究成果为电动汽车电机控制系统的工程开发提供了参考。 关键词:Stateflow;驱动电机控制;模式管

AI的内部世界的博客 58

Android InputChannel事件发送接收系统分析

安卓InputEvent发送接收流程系统分析,InputChannel创建过程,事件发送接收过程。

qq_36063677的博客 2136

基于STC8A8K64S4A12的掉电唤醒定时器应用,极大降低产品功耗

通过合理的配置和编程,我们可以充分发挥单片机的功能,实现能效优化和电池寿命延长的目标。其中,掉电唤醒定时器是其一项重要功能,可以在系统处于睡眠状态时通过定时器的计时功能进行唤醒,从而实现对系统功耗的有效控制。使用STC8A8K64S4A12的掉电唤醒定时器功能,我们可以在不影响系统正常工作的情况下,大大降低产品的功耗。通过以上的代码,我们实现了一个简单的掉电唤醒定时器功能。当系统进入睡眠模式后,定时器开始计时,到达预设的唤醒时间后,定时器中断被触发,系统从睡眠模式唤醒,执行相应的操作。

CodeSageX的博客 1090

Android 切换手势和按键、底部recent键事件传递

Android系统切换手势和按键 涉及到的模块 settings、systemUI、framework、launcher 1、settings 点击选择框,选择手势或者按键。切换时实际上是操作overlay,覆盖掉framework里的默认设置 2、systemUI 界面显示或者隐藏三大键 3、framework 默认全屏手势,设置中切换时,rro覆盖framework默认配置。 4、launcher launcher内部判断手势和按键,实现具体功能,比如recentui界面的呼出方式 ......

纵容的博客 8699

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Android之Input子系统事件分发流程

一、Android4.2系统服务侧 1.服务端注册过程 frameworks/base/core/java/android/view/ViewRootImpl.java public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { mInputChannel = new I

程序改变生活 2万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Hammer.js分析(三)——input.js

input.js是所有input文件夹中类的父类,浏览器事件绑定、初始化特定的input类、各种参数计算函数。 Input父类和其子类就是在做绑定事件,各种参数计算、整合、设置等返回自定义事件对象,交给识别器的相关对象使用。 一、Input父类 Input相当于一个抽象类,对象中总共有3个方法 1)handler(ev) 这相当于一个抽象方法,在上图中的6个子对象里,都会实现这个方法...

weixin_34320724的博客 265

Android Input之InputChannel

mWindowSession.addToDisplay()最终会调用WMS的addWindow()方法。

探索未知,成就非凡 973

inputreader到inputdispacther到channel

void InputReader::loopOnce() { ......  mQueuedListener->flush(); ...... } 到void QueuedInputListener::flush() {     size_t count = mArgsQueue.size();     for (size_t i = 0; i         Notif

u013308744的专栏 633

Launcher功能入口

具体见https://blog.csdn.net/a396604593/article/details/126060850。参考文章:https://blog.csdn.net/TSK_Amine/article/details/120362380。src/com/android/launcher3/PagedView.java中,onTouchEvent处理切页事件。Google BTS报错会检测代码漏洞,报错中也会有相应的修改方案,需要合入framework后编译jar包给到apk。

纵容的博客 6042

InputEventReceiver.finishInputEvent崩溃问题

查询到AOSP的用法实例frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt。怀疑是mWorkThread.getLooper()不是主线程导致的,替换了Looper.getMainLooper()再测试,就没有出现异常崩溃问题了。开发需求是监听所有屏幕的触摸事件,这样可以监听三指或者四指滑动的手势,做一些手势的动画。

267
上一篇: 千里马社群学习初次尝试-如何解决安卓偶现类黑屏,闪黑等问题
下一篇: 千里马第二次社群学习-分享你的framework成长学习经验
千里马学框架
博客等级 码龄6年 1万+粉丝 650原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值