ANR基础 - Input系统

news/2024/7/7 20:20:34

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 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://lihuaxi.xjx100.cn/news/1139373.html

相关文章

redis从零开始(1)----基本类型:string/hash/list

认识redis NoSQL Nosql not only sql&#xff0c;泛指非关系型数据库&#xff0c;与之相对的是RDBMS(Relational Database Management System)&#xff0c;即关系型数据库 关系型数据库&#xff1a;列行&#xff0c;同一个表下数据的结构是一样的。 非关系型数据库&#xff…

【Linux】常见指令

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情 &#x1f6f8;C专栏&#xff1a;Linux修炼内功基地 家人们更新不易&#xff0c;你们的&#x1f44d;点赞&#x1f44d;和⭐关注⭐…

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

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

「AI之劫」:当机器超越人类底线,正在侵犯我们的创造力和道德

随着AI技术的不断发展&#xff0c;AI生成内容&#xff08;AIGC&#xff09;已经成为数字娱乐、商业营销和学术研究等领域的热门话题。随着人工智能技术的不断发展越来越多的领域开始应用AI技术&#xff0c;其中之一就是内容生成领域。 AIGC全称为AI-Generated Content, 指基于生…

SpringCloud_服务调用_Ribbon概述以及使用(一)

SpringCloud_负载均衡_Ribbon(一) 概述 Ribbbon负载均衡演示 Ribbbon核心组件IRule Ribbbon负载均衡算法 概述 Ribbbon是一套客户端 负载均衡的工具 提供客户端的软件负载均衡算法和服务调用 地址&#xff1a; https://github.com/Netflix/ribbon/wiki/Getting-Started 目前这几…

基于Web的智慧储能电站3D可视化管理平台

电能作为现代社会的运转和发展的基础&#xff0c;是民生最基本的保障&#xff0c;其稳定性对国家经济发展至关重要。 建设背景 电力系统是一个稳态平衡系统&#xff0c;发电站的总发电功率需要等于用户端的总发电功率。如果两者不一致&#xff0c;就会导致整个电力系统的不稳…

yolo v8

这个系列代码被封装的非常的精致&#xff0c;对二次开发不太友好&#xff0c;虽然也还是可以做些调节 模型的导出 有三种方式试过&#xff0c;都可以导出onnx的模型 1. 用yolov8 源码来自&#xff1a;ultralytics\yolo\engine\exporter.py (不固定尺寸) yolo export modelpa…

【PySide || PyQT】QTreeWidget和QSTackedWidget的互动

1 QTreeWidget TreeWidget 控件的 ObjectName 是 tree 1.1 设置默认选项 在 TreeWidget 和 StackedWidget 互动的时候这是有必要的 也可以通过 QtDesigner 将 StackedWidget 某一页展示出来&#xff08;翻到那一页就行&#xff0c;不过 TreeWidget 的节点不会高亮&#xff0c…