ANR基础 - Input系统

news2024/11/27 16:53:14

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 系列文章目录
  • 前言
  • 一、Input系统概述
  • 二、整体框架
    • 1.整体框架类图
    • 2.核心启动过程
      • 2.1 initialize
      • 2.1 InputManager.start
  • 三、InputReader线程
    • 3.1 EventHuab
    • 3.2 InputReader核心流程
  • 四、InputDispatcher线程
    • 4.1 核心方法
    • 4.2 小节
  • 五、Input系统之UI线程
  • 六、Input事件处理全过程
    • 6.1 整体框架图
    • 6.2 交互过程
  • 总结


前言


一、Input系统概述

当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件。可见,输入系统在整个过程起到承上启下的衔接作用。

Input模块的主要组成:
Native层的InputReader负责从EventHub取出事件并处理,再交给InputDispatcher;
Native层的InputDispatcher接收来自InputReader的输入事件,并记录WMS的窗口信息,用于派发事件到合适的窗口;
Java层的InputManagerService跟WMS交互,WMS记录所有窗口信息,并同步更新到IMS,为InputDispatcher正确派发事件到ViewRootImpl提供保障;

二、整体框架

1.整体框架类图

在这里插入图片描述

2.核心启动过程

InputManager.cpp 核心代码如下:

2.1 initialize

void InputManager::initialize() {
    //创建线程“InputReader”
    mReaderThread = new InputReaderThread(mReader);
    //创建线程”InputDispatcher“
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
        Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

初始化的主要工作就是创建两个能访问Java代码的native线程:

创建线程“InputReader”
创建线程”InputDispatcher“

2.1 InputManager.start

status_t InputManager::start() {
    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ...
    return OK;
}

该方法的主要功能是启动两个线程:

启动线程“InputReader”
启动线程”InputDispatcher“

三、InputReader线程

3.1 EventHuab

EventHub采用INotify + epoll机制实现监听目录/dev/input下的设备节点

3.2 InputReader核心流程

在这里插入图片描述
InputReader整个过程涉及多次事件封装转换,其主要工作核心是以下三大步骤:

  1. getEvents:通过EventHub(监听目录/dev/input)读取事件放入mEventBuffer,而mEventBuffer是一个大小为256的数组, 再将事件input_event转换为RawEvent;
  2. processEventsLocked: 对事件进行加工, 转换RawEvent -> NotifyKeyArgs(NotifyArgs)
  3. QueuedListener->flush:将事件发送到InputDispatcher线程, 转换NotifyKeyArgs -> KeyEntry(EventEntry)

InputReader线程不断循环地执行InputReader.loopOnce(), 每次处理完生成的是EventEntry(比如KeyEntry, MotionEntry), 接下来的工作就交给InputDispatcher线程。

四、InputDispatcher线程

4.1 核心方法

用一张图来整体概况InputDispatcher线程的主要工作:
在这里插入图片描述
图解:
1.dispatchOnceInnerLocked(): 从InputDispatcher的mInboundQueue队列,取出事件EventEntry。另外该方法开始执行的时间点(currentTime)便是后续事件dispatchEntry的分发时间(deliveryTime)
2.dispatchKeyLocked():满足一定条件时会添加命令doInterceptKeyBeforeDispatchingLockedInterruptible;
3.enqueueDispatchEntryLocked():生成事件DispatchEntry并加入connection的outbound队列
4.startDispatchCycleLocked():从outboundQueue中取出事件DispatchEntry, 重新放入connection的waitQueue队列;
5.InputChannel.sendMessage通过socket方式将消息发送给远程进程;
6.runCommandsLockedInterruptible():通过循环遍历地方式,依次处理mCommandQueue队列中的所有命令。而mCommandQueue队列中的命令是通过postCommandLocked()方式向该队列添加的。

InputDispatcher中有一个线程,死循环执行dispatchOnce方法,该方法负责分发消息给APP侧,以及接受APP侧的返回并执行相关以后,最后执行ANR的判断。所以dispatchOnce属于整个的流程的核心,也是ANR处理流程的核心,所以我们重点了解下dispatchOnce这个方法。如下图所示:
在这里插入图片描述
dispatchOnce中主要完成四件事:

  1. dispatchOnceInnerLocked()方法负责把收到的输入信号分发给APP处理,发送成功会加入到InputDispatcher里的waitQueue队列和AnrTracker.cpp里的mAnrTimeouts。
  2. haveCommandsLocked()中查看队列中是否有任务,如果有就执行任务。这些任务就是执行doDispatchCycleFinishedCommand方法,该方法中,会根据收到的完成信号,完成对应的事件从waitQueue和mAnrTimeouts中移除的处理。
  3. processAnrsLocked中会进行一些逻辑判断,如果符合条件,则会触发ANR流程。
  4. pollOnce进入休眠,等待下一次的循环。

4.2 小节

InputReader读到输入事件后,就会传递到InputDispatcher,整个超时流程也都是由其负责的。InputDispatcher中主要是dispatchOnce方法来负责,它跑在单独的线程上。
首先它把收到的输入信号分发给APP一侧并记录到mAnrTimeouts集合上;
然后查看是否有APP侧传递过来的任务,如果有就执行,该任务就是把对应的输入事件从mAnrTimeouts集合中移除;
再然后判断mAnrTimeouts集合中是否有超时的事件,如果有就走ANR逻辑;
最后,一轮逻辑走完了,进入休眠,等待下一轮的唤醒。

五、Input系统之UI线程

在InputDispatcher的过程调用到InputChanel通过socket与远程进程通信,这里看下这个socket是如何建立的。

对于InputReader和InputDispatcher都是运行在system_server进程; 用户点击的界面往往可能是某一个app,而每个app一般地都运行在自己的进程,这里就涉及到跨进程通信,app进程是如何与system进程建立通信。

要解答这些问题,就要从Activity最基本的创建过程寻找。我们都知道一般地Activity对应一个应用窗口, 每一个窗口对应一个ViewRootImpl。

ViewRootImpl的setView()过程:
创建socket pair,作为InputChannel:

  • socket服务端保存到system_server中的WindowState的mInputChannel;
  • socket客户端通过binder传回到远程进程的UI主线程ViewRootImpl的mInputChannel;

IMS.registerInputChannel()注册InputChannel,监听socket服务端:

  • Loop便是“InputDispatcher”线程的Looper;
  • 回调方法handleReceiveCallback。

用一张图来整体概况UI线程跨进程通信的主要工作:
在这里插入图片描述
首先,通过openInputChannelPair来创建socket pair,作为InputChannel:

  • socket服务端保存到system_server中的WindowState的mInputChannel;
  • socket客户端通过binder传回到远程进程的UI主线程ViewRootImpl的mInputChannel;

紧接着,完成了两个线程的epoll监听工作:

  • IMS.registerInputChannel(): “InputDispatcher”线程监听socket服务端,收到消息后回调InputDispatcher.handleReceiveCallback();
  • setFdEvents(): UI主线程监听socket客户端,收到消息后回调NativeInputEventReceiver.handleEvent().

有了这些“InputDispatcher”和“UI”主线程便可以进行跨进程通信与交互。

六、Input事件处理全过程

6.1 整体框架图

在这里插入图片描述

6.2 交互过程

用一张图展示交互过程,主要是通过一对socket方式来通信。 当input时间分发到app端, 便进入了InputEventReceiver.dispatchInputEvent()过程.
在这里插入图片描述
图解:

  1. InputDispatcher线程调用InputPublisher的publishKeyEvent向UI主线程发送input事件;
  2. UI主线程接收到该事件后,调用InputConsumer的consumeEvents来处理该事件, 一路执行到ViewRootImpl.deliverInputEvent()方法;
  3. UI主线程经过一系列的InputStage来处理, 当事件分发完成,则会执行finishInputEvent()方法.再进一步调用InputConsumer::sendFinishedSignal 告知InputDispatcher线程该时事件已处理完成.
  4. InputDispatcher线程收到该事件后, 执行InputDispatcher::handleReceiveCallback();最终会调用doDispatchCycleFinishedLockedInterruptible()方法 ,将dispatchEntry事件从等待队列(waitQueue)中移除.

总结

简单总结和回顾以上文章的内容:

  • InputReader线程:通过EventHub从/dev/input节点获取事件,转换成EventEntry事件加入到InputDispatcher的mInboundQueue。
  • InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connection的outboundQueue队列。再然后开始处理分发事件,取出outbound队列,放入waitQueue.
  • UI线程:创建socket pair,分别位于”InputDispatcher”线程和focused窗口所在进程的UI主线程,可相互通信:
  1. UI主线程:通过setFdEvents(), 监听socket客户端,收到消息后回调NativeInputEventReceiver();
  2. “InputDispatcher”线程: 通过IMS.registerInputChannel(),监听socket服务端,收到消息后回调handleReceiveCallback;

参考:
http://gityuan.com/2016/12/31/input-ipc/
Input类型ANR产生原理讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅析一下PTPD

浅聊一下PTPD 文章目录 浅聊一下PTPD1.什么是PTPD2.PTPD源码浅析一下1.src文件1.arith.c2.bmc.c3.constant.h 和 datatypes.h4.display.c5.management.c6.protocol.c7.ptp_datatypes.h8.ptp_primitives.h9.ptp_timers.c10.ptpd.c11.signaling.c12.timedomain.c 2.def文件夹3.de…

ROS:gazebo创建仿真地图,turtlebot3加载仿真地图进行建图,生成yaml和pgm地图信息

一.安装turtlebot3 Ubuntu18.04 实现&#xff1a;安装turtlebot3功能包、虚拟机与机器人之间的网络配置、测试机器人Cartographer建图_Charlesffff的博客-CSDN博客 二.安装gazebo ROS18.04&#xff1a;安装gazebo&#xff0c;下载模型_gazebo下载模型_Charlesffff的博客-CSD…

Linux 设备驱动程序(二)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核&#xff08;一&#xff09; 深入理解 Linux 内核&#xff08;二&#xff09; Linux 设备驱动程序&#xff08;一&#xff09; Linux 设备驱动程序&#xff08;二&#xff09; Linux设备驱动开发详解 文章目录 系列文章目…

大型央企集团财务经营分析框架系列(三)

01集团经营管理分析的切入点 集团经营管理分析的切入点往往是从财务分析开始。 往往在一家企业里面&#xff0c;财务方面的信息化建设是要早于其它方面的信息化建设的&#xff0c;业务标准化程度比较高&#xff0c;数据标准化程度也比较高&#xff0c;分析框架也相对成熟。 …

栈和队列的相关功能实现及其基础应用

前言&#xff1a;栈和队列是常见的数据结构&#xff0c;它们在计算机科学中被广泛应用。栈和队列都是一些元素的集合&#xff0c;它们的主要区别在于数据的组织方式和访问顺序。在栈中&#xff0c;元素的添加和删除都在同一端进行&#xff0c;称为栈顶&#xff0c;而在队列中&a…

PMP考试100个主要知识点

1.一个项目在启动阶段会进行量级估算&#xff0c;准确范围是-50至100%。2000版的量级估算准确度为&#xff1a;-25%到75%。 2.质量控制通常先于范围确认执行&#xff0c;但这两个过程可以并列进行参考 3.Cost-plus-fixed-fee(CPFF)成本加固定费用合同。成本补偿型合同包括成本加…

快速入门ChatGPT和AIGC:底层原理、热门工具、行业现状【我们能做什么】

最近大家热议的ChatGPT和AI绘画工具的底层技术原理是什么&#xff1f;是如何发展到现在的&#xff1f;有哪些应用场景、热门工具&#xff1f;AIGC产业上下游有哪些公司&#xff1f;作为普通用户&#xff0c;我们还能接触哪些应用AI技术打造的商业解决方案&#xff1f;…… 我们…

微信小程序 录音+播放组件封装

展示 长按录音 松开结束录音 点击播放 再次点击暂停 再次点击继续播放 展示效果&#xff1a; 录音功能 录音功能&#xff08;手指按下开始录音 手指松开结束录音&#xff09;&#xff1a; 使用wx原生录音功能在 component 外新建 wx.getRecorderManager() RecorderManager…

国巨 :硬件设计基础60条

硬件设计是现代科技发展中至关重要的领域之一。它涵盖了从微电子器件到复杂的系统级设计的各个方面&#xff0c;是现代电子产品的核心。在这篇文章中&#xff0c;我将介绍60个基础概念&#xff0c;这些概念是硬件设计工程师必备的知识&#xff0c;并且是设计出高质量硬件的关键…

【web】学习ajax和fetch

1/什么是ajax ajax 全名 async javascript and XML(异步JavaScript和XML) 是前后台交互的能⼒。 也就是我们客户端给服务端发送消息的⼯具&#xff0c;以及接受响应的⼯具。 在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新。而传统的网页(不使用 Ajax)如果需…

设计师必备的5个素材库,马住

今天就告诉大家设计师都是去哪些网站找素材&#xff0c;分享五个网站&#xff0c;解决你80%的设计素材&#xff0c;建议收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/?vNTYxMjky 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报…

成为一名黑客需要学什么

想成为一名专业黑客&#xff0c;但不知道从哪里学起”很多人向盾叔问过这个问题&#xff0c;今天盾叔就为你介绍成为专业黑客必须学习的十个方面的知识&#xff0c;希望能为迷惘中的你指明方向。 一、基本的计算机知识 把它列为第一条&#xff0c;相信很多人肯定会觉得不以为…

Java虚拟机快速入门 | JVM引言、JVM内存结构、直接内存

目录 一&#xff1a;JVM引言 1. 什么是 JVM ? 2. 常见的 JVM 3. 学习路线 二&#xff1a;JVM内存结构 1. 程 序 计 数 器&#xff08;PC Register&#xff09; 2. 虚 拟 机 栈&#xff08;JVM Stacks&#xff09; 3. 本 地 方 法 栈&#xff08;Native Method Stacks&…

Vscode C++环境配置

多文件编译 打开设置搜索coderunner 找到Executor Map 加入-I目录名 目录名/*.cpp 调试 点击调试以后会产生tasks.json文件&#xff0c;加入链接文件和库文件

map用法以及特殊值的情况

map用法以及特殊值的情况 一、map用法的说明 map(callbackFn, thisArg); // callbackFn回调函数&#xff0c;thisArg可选 callbackFn是个回调函数&#xff0c;该回调函数的参数按照顺序为element&#xff08;当前正在处理的元素&#xff09;&#xff0c;index&#xff08;正…

WPF MaterialDesign 初学项目实战(1)首页搭建

前言 最近在学WPF&#xff0c;由于人比较烂&#xff0c;有一个星期没怎么动代码了。感觉有点堕落。现在开始记录WPF项目&#xff0c;使用MaterialDesignInXamlToolkit。 环境搭建 如果没下载MaterialDesign 的源码 github源码运行 在Nuget里面引入MaterialDesign Materia…

数字孪生技术在环境保护领域怎样应用?

近年来&#xff0c;环境保护成为全球范围内的热点话题&#xff0c;各国都在积极探索创新的解决方案。其中&#xff0c;数字孪生技术的出现为环境保护带来了全新的机遇和挑战。数字孪生技术将物理世界与数字世界相结合&#xff0c;通过精确的模拟和实时数据分析&#xff0c;为环…

华为ensp 防火墙的基础配置

拓扑图&#xff1a; [FW3-zone-isp1]set priority 12 #配置防火墙优先级 步骤一 #首先进入防火墙需要输入默认账号和密码&#xff0c;必须修改密码。 [USG6000V1] undo in en #关闭提示。 #先配置ip。 [USG6000V1]ip route-static 0.0.0.0 0.0.0.0 64.1.1.10 #配置去往外网的默…

【Redis】Redisson入门以及Redisson可重入锁的lua脚本实现

目录 一、Redisson介绍 二、Redisson的入门 1、引入依赖 2、配置客户端 3、使用锁 三、Redisson可重入锁的原理 1、原理 2、实现 3、lua脚本保证原子性 1.获取锁 2.释放锁 一、Redisson介绍 在之前的文章里我们通过redis中的setn实现了一个简单的分布式锁以及解决了…

远程协助软件推荐,有哪些远程协助工具?

Win10、11自带远程协助工具-快速助手 Win10、11的快速助手使用非常简单。只要在左下角的搜索框搜索一下就可以找到了。 我们都知道&#xff0c;Windows带有远程桌面RDP功能&#xff0c;而快速助手是通过Windows的远程连接机制实现的。所以在使用前&#xff0c;被控端需要开启系…