Android ANR视角InputDispatcher

作者:王小二

前言

有好多人向我咨询过Input ANR问题,说实话,我也是一直无法彻底的解释清楚,我下决心要彻底搞懂这块知识点。

话不多说先上图

640?wx_fmt=png

一个event的正常流程

InputReader线程

1.InputReader线程一旦发现有新的event,判断mInBoundQueue是否为空,如果为空,设置wakeup = true

2.添加event到mInBoundQueue,如果wakeup==true,唤醒InputDispatcher的mLooper

InputDispatcher线程

1.没有事做的时候,mLooper.pollOnce(timeoutMillis)休眠, timeoutMillis为下次唤醒的delay时间。

2.mLooper被唤醒

a.发现mPendingEvnet为空且mInBoundQueue不为空,从mInBoundQueue获取一个event,并赋值给mPendingEvnet,走到第3步 b.发现mPendingEvnet不为空,走第3步 c.发现mPendingEvnet为空且mInBoundQueue为空,回到第1步休眠

3.检查当前的window是否可以接收mPendingEvnet,正常情况下是OK的,异常的情况,我们后面讨论。

4.通过InputChannel分发mPendingEvnet到APP层后, mPendingEvnet保存到waitQueue

5.发送成功后releasePendingEventLocked(mPendingEvnet == null),并将mLooper的nextWakeupTime设置LONG_LONG_MIN,然后回到第1步。

6.当App层处理完event后会发送一个finish信号过来,然后移除waitQueue中event,并唤醒mLooper,触发第2步

Input ANR的发生的原因:主线程的卡顿

怎么理解这句话如何导致的ANR?

主线程卡顿主要是导致的InputDispatcher线程中的正常流程第6步无法完成。

假设event1的没有完成第6步,这时候来了一个event2这个流程是怎么样子的:

第1步,第2步是一样的

第3步:

waitQueue不为空,导致checkWindowReadyForMoreInputLocked返回值不为空,触发handleTargetsNotReadyLocked,然后将当前时间+5s作为mInputTargetWaitTimeoutTime,并设置mInputTargetWaitTimeoutTime为mLooper下一次唤醒的时间

std::string reason = checkWindowReadyForMoreInputLocked(currentTime,	touchedWindow.windowHandle, entry, "touched");	
if (!reason.empty()) {//reason不等于空	injectionResult = handleTargetsNotReadyLocked(currentTime, entry,	NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());	goto Unresponsive;	
}	std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,	const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,	const char* targetType) {	//省略好多代码,因为不止一种请款,我们只分析一种	if (!connection->waitQueue.isEmpty()	&& currentTime >= connection->waitQueue.head->deliveryTime	+ STREAM_AHEAD_EVENT_TIMEOUT) {	return StringPrintf("Waiting to send non-key event because the %s window has not "	"finished processing certain input events that were delivered to it over "	"%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms.",	targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,	connection->waitQueue.count(),	(currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);	}	return "";	
}	int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,	const EventEntry* entry,	const sp<InputApplicationHandle>& applicationHandle,	const sp<InputWindowHandle>& windowHandle,	nsecs_t* nextWakeupTime, const char* reason) {	//省略好多代码	if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {	//省略好多代码	//设置第一次卡顿的flag后面进来就不会设置了	mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;	mInputTargetWaitStartTime = currentTime;	//设置mInputTargetWaitTimeoutTime为当前时间+5s	mInputTargetWaitTimeoutTime = currentTime + timeout;//timeout = 5s	//省略好多代码	}	//如何当前的时候大于mInputTargetWaitTimeoutTime就出现ANR,默认第一次进来是走else	if (currentTime >= mInputTargetWaitTimeoutTime) {	onANRLocked(currentTime, applicationHandle, windowHandle,	entry->eventTime, mInputTargetWaitStartTime, reason);	*nextWakeupTime = LONG_LONG_MIN;	return INPUT_EVENT_INJECTION_PENDING;	} else {	//将mInputTargetWaitTimeoutTime下一次wakeup的时间	if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {	*nextWakeupTime = mInputTargetWaitTimeoutTime;	}	return INPUT_EVENT_INJECTION_PENDING;	}	
}

第4步:

因为无法发送event2,releasePendingEventLocked就不会触发,mPendingEvnet就会保留发送失败的event2。

第5步:

情况A:在mInputTargetWaitTimeoutTime之前event1完成了常规的操作中的第6步,发送finish信号,就会唤醒mLooper,然后继续处理mPendingEvnet,也就是event2,因为waitQueue已经为空了,那么event2就会按照正常流程的处理了

情况B:在mInputTargetWaitTimeoutTime之前event1没有完成常规的操作第6步,这时候mLooper被handleTargetsNotReadyLocked中设置的wakeuptime所唤醒,然后继续处理mPendingEvnet,也就是event2,因为waitQueue不为空,event1还在,所以又会触发handleTargetsNotReadyLocked,这一次只会走以下代码,然后触发ANR

if (currentTime >= mInputTargetWaitTimeoutTime) {	onANRLocked(currentTime, applicationHandle, windowHandle,	entry->eventTime, mInputTargetWaitStartTime, reason);	*nextWakeupTime = LONG_LONG_MIN;	return INPUT_EVENT_INJECTION_PENDING;	}

总结

Input ANR是所有ANR中最难理解的一种ANR,我只分析了其中一种情况的Input ANR,想要了解所有Input ANR,只需要在源码中搜索handleTargetsNotReadyLocked出现的位置,结合代码看就知道了。

记住一句话:InputDispatcher永远只能单线程处理一个mPendingEvent,如果分发失败,下一次会继续分发同一个mPendingEvent。


640?wx_fmt=jpeg

扫码或长按关注

回复「 加群 」进入技术群聊

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

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

相关文章

频繁跳槽,这谁顶得住~

最近应该是校招的时候&#xff0c;相信很多人都面临择业的问题&#xff0c;正念同学的文章&#xff0c;记录了自己一个嵌入式工程师这几年找工作换工作的经历。加我好友的都知道&#xff0c;我这几天发了一个朋友圈&#xff0c;说不要乱跳槽&#xff0c;我想表达的是&#xff0…

java script object_javascript Object与Array用法

引用类型&#xff1a;引用类型是一种数据结构&#xff0c;用于将数据和功能组织在一起。引用类型的值是引用类型的一个实例。一、ObjectECMAScript中的对象其实就是一组数据和功能的结合。Object类型其实是所有它的实例的基础&#xff0c;换句话说&#xff0c;Object类型所有具…

Linux内核编程广泛使用的前向声明(Forward Declaration)

前向声明编程定律先强调一点&#xff1a;在一切可能的场景&#xff0c;尽可能地使用前向声明(Forward Declaration)。这符合信息隐蔽的原则。一个例子regmap那么前向声明究竟是个什么鬼&#xff1f;在内核写代码和看代码的童鞋&#xff0c;经常发现Linux内核里面充斥着这样的代…

Top 10 Project Management Software

转载于:https://www.cnblogs.com/shy1766IT/p/7082910.html

java 二维数组 floyd_Floyd算法(一)之 C语言详解

本章介绍弗洛伊德算法。和以往一样&#xff0c;本文会先对弗洛伊德算法的理论论知识进行介绍&#xff0c;然后给出C语言的实现。后续再分别给出C和Java版本的实现。弗洛伊德算法介绍和Dijkstra算法一样&#xff0c;弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短…

南拳北腿

昨晚&#xff0c;熬夜看了篮球综艺节目&#xff0c;《我要打篮球》&#xff0c;实在话&#xff0c;我是林书豪的球迷&#xff0c;所以我肯定是希望林书豪球队能获胜&#xff0c;最后也如我所愿&#xff0c;两场3v3&#xff0c;电光飞侠都是在处于被动的情况下完成自我救赎&…

C语言const 关键字

面试的时候&#xff0c;应该有遇到const相关的&#xff0c;毕竟也是学习中的一个知识点&#xff0c;看完我们这篇文章&#xff0c;我觉得你应该可以在面试中完完全全的吃透const这个点。const和变量const uint32_t hello 3;编译的时候&#xff0c;编译器就知道了 hello 这个变…

pandas 第一行_Pandas数据预处理相关经验

在这里记录一些平常用的pandas操作以供参考。学习相关操作的最好方法还是找官方的文档最好&#xff0c;否则就会产生百度1小时&#xff0c;查文档3分钟的尴尬处境&#xff0c;之前为了找python里类似 in 的操作搜了半天资料也没找到&#xff0c;结果文档里就是 isin 函数就好了…

单机 amp; 弱联网手游 防破解、金币改动 简单措施

单机 &amp; 弱联网手游 防破解、金币改动 简单措施 手游经常使用破解方法 对于一个弱联网或者单机游戏&#xff0c;能够从下面方面去破解&#xff1a; 1、找得到存档文件的&#xff0c;直接破解改动存档文件。 2、找不到存档文件&#xff0c;就在游戏执行时借助一些软件来改…

Linux 内核通知链和例程代码

概念大多数内核子系统都是相互独立的&#xff0c;因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求&#xff0c;也即是让某个子系统在发生某个事件时通知其它的子系统&#xff0c;Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用&#x…

faster rcnn resnet_RCNN系列、Fast-RCNN、Faster-RCNN、R-FCN检测模型对比

RCNN系列、Fast-RCNN、Faster-RCNN、R-FCN检测模型对比一&#xff0e;RCNN问题一&#xff1a;速度经典的目标检测算法使用滑动窗法依次判断所有可能的区域。本文则预先提取一系列较可能是物体的候选区域&#xff0c;之后仅在这些候选区域上提取特征&#xff0c;进行判断。问题二…

啰嗦一二三

第一点之前有一个抽奖&#xff0c;抽取野火开发板的&#xff0c;我记得有很多人参加了&#xff0c;20号的时候&#xff0c;获奖的同学都主动找我填写了收获地址&#xff0c;但是有一个同学特别调皮&#xff0c;到今天都没有来找我&#xff0c;我等了好久好久&#xff0c;还是没…

go设置后端启动_为什么 Rubyists 应该考虑学习 Go

点击上方蓝色“Go语言中文网”关注我们&#xff0c;领全套Go资料&#xff0c;每天学习 Go 语言如今&#xff0c;越来越少的 Web 开发人员开始专注于 Ruby 之类的单一语言。我们针对不同的工作使用不同的工具。在本文中&#xff0c;Ayooluwa Isaiah 认为 Go 是 Ruby 的完美补充。…

生涯刚开始就要转会?

昨晚&#xff0c;12点快睡觉的时候&#xff0c;一个同学给我发来的问题&#xff0c;篇幅有点长&#xff0c;都是文字&#xff0c;看起来可能有点乏味&#xff0c;不过有耐性的同学还是瞄一下&#xff0c;应该很多人都有这样的迷茫期&#xff0c;我的解答也不一定完全正确&#…

Topaz Video AI 视频修复工具(内附安装压缩包win+Mac)

目录 一、Topaz Video AI 简介 二、Topaz Video AI 安装下载 三、Topaz Video AI 使用 最近玩上了pika1.0和runway的图片转视频&#xff0c;发现生成出来的视频都是有点糊的&#xff0c;然后就找到这款AI修复视频工具 Topaz Video AI。 一、Topaz Video AI 简介 Topaz Video…

python文本编辑器报错_notepad++编辑器中运行python程序时需要注意的编码格式

本篇文章给大家带来的内容是关于notepad编辑器中运行python程序时需要注意的编码格式&#xff0c;有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对你有所帮助。 语言&#xff1a;python3.4 文本编辑器&#xff1a;notepad 报错&#xff1a;SyntaxErro…

我不建议大家随便跳槽

突然的留言 我的微信好友很多&#xff0c;经常也有一些同学给我留言一些问题&#xff0c;当然能回答的我肯定会回答了&#xff0c;但是如果是非常难的技术问题&#xff0c;我一般会说&#xff0c;我要请教一下身边的朋友&#xff0c;昨晚准备睡觉的时候&#xff0c;收到一个同…

我是不建议随便跳槽的

突然的留言我的微信好友很多&#xff0c;经常也有一些同学给我留言一些问题&#xff0c;当然能回答的我肯定会回答了&#xff0c;但是如果是非常难的技术问题&#xff0c;我一般会说「我要请教一下身边的朋友」&#xff0c;我会非常刻意的去回避我技术很水的这个问题&#xff0…

ubuntu c++检测usb口事件变化_拆解报告:美式双USBA口充电插座

----- 充电头网拆解报告 第1441篇 -----最近充电头网拿到了一款美式插座&#xff0c;这款产品相比常规墙插插座&#xff0c;除了配有两个双脚AC插口外&#xff0c;还有两个USB-A接口&#xff0c;支持直接插上数据线即可为两台设备进行同时充电。其中两个USB-A口都支持最大5V4.8…

div不继承父类样式_Python编程思想(27):类的继承

-----------支持作者请转发本文-----------李宁老师已经在「极客起源」 微信公众号推出《Python编程思想》电子书&#xff0c;囊括了Python的核心技术&#xff0c;以及Python的主要函数库的使用方法。读者可以在「极客起源」 公众号中输入 160442 开始学习。-----------正文---…