当前位置: 首页 > news >正文

Android InputDispatcher 跨 Display 触摸事件丢失分析

每个 Display 都拥有自己独立且完整的生态,具体表现为各自管理着一组 Window。

1. 引子:从一个拖拽 Bug 说起

用户在 Launcher 中进行拖拽操作时,如果发生了 Display 焦点切换,拖拽的 View 会卡在屏幕上无法消失。

2. Android Input 管道全景

网上已有大量关于此主题的文章可供参考,本文仅作简要概述。

Android 输入事件的处理管道可分为五个层次:

  1. 硬件层:负责接收原始的触摸、按键等输入信号(例如,手指触摸屏幕产生一个触摸事件)。
  2. Linux 内核层:接收硬件信号,并将其封装成input_event结构,写入到如/dev/input/eventX之类的设备节点。
  3. Android Native 层:负责从设备节点(如/dev/input/eventX)读取原始的输入事件。
  4. Android Service 层:进行事件的解析、分发,并将其派发给上层的应用。
  5. 应用层:最终接收并消费处理输入事件。

3. TouchState:每个 Display 独立记账

3.1 mTouchStatesByDisplay 数据结构

mTouchStatesByDisplay是一个std::unordered_map,其键(key)为displayId。这意味着:

每个显示屏拥有自己独立的触摸状态,彼此互不干扰。

std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);

3.2 TouchState 结构体

struct TouchState { bool down; // 是否有手指按下? bool split; // 是否开启了分屏触摸? int32_t deviceId; // 是哪个输入设备的触摸?(防止多设备冲突) uint32_t source; // 输入源(触摸屏?鼠标?) int32_t displayId; // 是哪个显示屏的触摸? std::vector<TouchedWindow> windows; // 当前触摸了哪些窗口? std::vector<sp<InputWindowHandle>> portalWindows; // 穿过了哪些 portal window? std::vector<TouchedMonitor> gestureMonitors; // 手势监听器 };

4. 根因分析:findTouchedWindowTargetsLocked

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // ... done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); } int32_t InputDispatcher::dispatchMotionLocked(...) { injectionResult = findTouchedWindowTargetsLocked(...); }

当后续事件携带的displayIdmTouchStatesByDisplay中不存在对应的TouchState时,find(displayId)会返回end(),此时oldState不会从该 Display 获取到已有触摸状态

Step 1:拷贝旧状态

const TouchState* oldState = nullptr; TouchState tempTouchState; auto oldStateIt = mTouchStatesByDisplay.find(displayId); // 使用 displayId 查询 map if (oldStateIt != mTouchStatesByDisplay.end()) { oldState = &(oldStateIt->second); tempTouchState.copyFrom(*oldState); }

Step 2:判断是否是新手势

bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);

Step 3:设备切换检测

bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 && (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source || tempTouchState.displayId != displayId); // ← 此处 displayId 不同会触发!

如果switchedDevice == true且当前有手指按下(tempTouchState.down == true),则会执行以下逻辑:

if (switchedDevice && tempTouchState.down && !down && !isHoverAction) { ALOGI("Dropping event because a pointer for a different device is already down " "in display %" PRId32, displayId); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; // ← 直接丢弃事件! } Failed: // 1. 检查权限 if (injectionPermission != INJECTION_PERMISSION_GRANTED) { return injectionResult; // ← 直接返回,什么都不做 } // 2. 更新状态(但 wrongDevice 时不更新) if (!wrongDevice) { // 更新 mTouchStatesByDisplay // 但没有发送 CANCEL 事件! } return injectionResult;

问题所在:当后续事件的displayId与当前TouchState中记录的displayId不一致时,会使switchedDevicetrue。如果此时tempTouchState.down仍为true,且当前事件不是新的DOWN、也不是 Hover 事件,则该事件会进入Failed路径,并以INPUT_EVENT_INJECTION_FAILED结束处理。

进一步影响:该路径不会按照正常的触摸事件分发流程继续处理当前事件,因此原有拖拽手势无法正常完成后续的事件状态转换。如果拖拽 View 依赖后续的CANCEL事件清理自身状态,而该CANCEL又没有在后续流程中产生或送达应用层,就可能出现拖拽 View 残留在屏幕上的现象。

5. 调试方法

  • dumpsys input
http://www.cnnetsun.cn/news/4070597.html

相关文章:

  • 一文搞懂WeTextProcessing:让语音与文本处理项目告别数字乱码的归一化利器
  • Bitwarden:开源免费的跨平台密码管理器,端到端加密
  • 热门八股-JUC
  • 特斯拉Model 3如何以鲶鱼效应重塑中国新能源汽车产业格局
  • 江西五十铃新款D-MAX谍照解析:中期改款设计、动力与市场前瞻
  • 网盘直链下载怎么玩才不折腾?我的两年亲测与避坑记录
  • 微信读书网页版字体自定义:CSS注入与Tampermonkey脚本实战
  • Linux Shell特殊符号完全指南:从重定向到管道,掌握命令行核心语法
  • 请求绑定与校验
  • 被苹果放弃的老电脑,我用一个免费工具让它重获新生
  • sva日常学习0
  • RuoYi-Vue Pro 完整上手指南:1 小时搭建带权限与审批的企业级后台
  • AI水印技术解析:从SynthID到C2PA标准,开发者如何管理水印可见性
  • 5分钟跑起跨平台QSP播放器:JavaQuestPlayer完整上手与实测体验
  • PCSX2免费开源模拟器完整指南:如何在家用电脑上高清重玩PS2经典游戏
  • 166、Zephyr RTOS调试与测试基础:调试工具链
  • 虚拟机中OpenFOAM-v2012与Paraview完整安装配置指南
  • Cursor免费使用受阻?一份解决设备限制与机器ID重置的完整指南
  • IoT OTA差分升级:bspatch减小固件体积
  • 如何让《暗黑破坏神2》在现代电脑上满帧运行?D2DX宽屏补丁完整上手指南
  • 每天省下一小时重复劳动的炉石传说插件:HsMod到底能帮你做什么
  • 从一脸懵到轻松绕过:Awesome-WAF 帮你 3 步吃透 Web 应用防火墙攻防测试
  • Ryujinx模拟器终极调优攻略:从装不上到丝滑畅玩的完整实操
  • 系统架构设计师考试模拟题库(下):案例分析与论文指导
  • League Akari 英雄联盟客户端工具箱终极指南:自动选人、流程自动化与对局洞察一网打尽
  • Qwen3.8-27B多模态大模型实战:从技术验货到工程化部署全指南
  • Outfit字体实操指南:免费可商用的几何无衬线字体如何一站式打通品牌视觉统一?
  • 汽车测试谍照背后的产品生命周期管理逻辑与市场策略
  • 构建游戏指令智能系统:从数据建模到安全执行的工程实践
  • YOLO 涨点改进|全网独家复现单通道红外小目标增强 光伏板鸟粪微弱热斑识别、分布式光伏无人机巡检全场景有效涨点