ANR基础 - Input系统

本文深入解析Android的Input系统,从InputReader和InputDispatcher线程的工作机制,到Input事件如何在UI线程中处理,详述了Input事件的全处理流程,对于理解ANR中的Input超时机制至关重要。
摘要由CSDN通过智能技术生成

系列文章目录

ANR概述

ANR基础篇 - Trace.txt文件分析

ANR基础 - Input系统

ANR基础篇 - 相关系统知识简介

ANR原理篇 - ANR原理总览

ANR原理篇 - ANR弹框是如何显示出来的

ANR原理篇 - service/broadcast/provider超时机制

ANR原理篇 - Input超时机制

ANR原理篇 - ANR信息收集过程

ANR实战案例 - 通用方法总结

ANR实战案例 - 锁优化

ANR实战案例 - FCM拉活启动优化

ANR实战案例1 - Google广告导致ANR解决

ANR实战案例 2 - 不同线程状态ANR示例

ANR实战案例3 - 应用在部分低端机ANR优化案例

ANR工具篇 - 监控工具

ANR工具篇 - 分析工具



前言


一、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;

参考:
事件处理全过程
Input类型ANR产生原理讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值