作者:王小二

前言

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

话不多说先上图

一个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。


扫码或长按关注

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

Android ANR视角InputDispatcher相关推荐

  1. Android ANR

    ANRs ("Application Not Responding"),意思是"应用没有响应". 1)什么引发了ANR? 在Android里,应用程序的响应性是 ...

  2. android anr 产生的类型及原因

    android anr 产生的条件 android 系统中anr的本质是主线程无法响应.而导致主线程无法响应的原因大致如下: 主线程请求网络资源,数据库访问或者io访问,这些操作都是耗时操作,主线程处 ...

  3. Android ANR 搜集

    Android ANR排查 应用层一般如下情况下需要查看log 1) 程序异常退出 , uncaused exception 2) 程序强制关闭 ,Force Closed (简称FC) 3) 程序无 ...

  4. 看完这篇 Android ANR 分析,就可以和面试官装逼了!

    点击上方"何俊林",马上关注,每天早上8:50准时推送 真爱,请置顶或星标 本文转载自公号玉刚说,原创作者htkeepmoving,原文链接:https://www.jianshu ...

  5. Android:ANR问题是什么和如何避免

    文章目录 前言 一.ANR是什么? 二.ANR时间规定 1.Service Timeout 2.BroadcastQueue Timeout 3.ContentProvider Timeout 4.I ...

  6. Android ANR 问题第二弹------Input超时实战问题解析上

    在前面的Android ANR 问题第二弹一文中,我们简诉了Android Input超时的原因,我们了解到系统Input系统分发Input的事件时如果有5s超时会触发应用ANR.在实际开发测试中,我 ...

  7. 转 android anr 分析示例,[摘]Android ANR日志分析指南之实例解析

    前文<[摘]Android ANR日志分析指南>也摘抄了如何分析,接下来通过实例解析. 一.主线程被其他线程lock,导致死锁 waiting on <0x1cd570> (a ...

  8. Android 系统(135)---Android anr 分析步骤总结

    Android anr 分析步骤总结 前言:最近经手了比较多的anr问题,声明经手不是解决,只是从log上推断造成anr的原因,以此作为根据转交给对应的人来处理. 1. ANR简介 ANR全名Appl ...

  9. Android ANR是什么?

    Android ANR 前言 一.ANR产生的原因 二.如何尽量避免ANR? 前言 ANR:Application Not Responding,应用程序无响应 一.ANR产生的原因 Android系 ...

最新文章

  1. iOS12系统应用发送普通邮件构建邮件
  2. 二十八、Pyspider 爬取链家网
  3. CNN结构:用于检测的CNN结构进化-结合式方法
  4. swift5表情键盘项目封装
  5. Linux 系统配置Java Idea Tomcat 全过程
  6. 【POJ - 3169】 Layout(差分约束+spfa)(当板子记?)
  7. OC之ARC环境中的循环strong问题
  8. 小度智能音箱维修点_小度智能音箱无法唤醒怎么办
  9. Decoder is not a @Sharable handler, so can't be added or removed multiple times
  10. mysql水平拆分 hash_常用的数据库表水平拆分方案
  11. 【题解】洛谷P4158 [SCOI2009] 粉刷匠(DP)
  12. 地图不显示_图灵搜不显示地图,软件打开一片空白,怎么解决?
  13. 让人耳目一新的四款高质量软件,简约又实用,每一款都值得收藏
  14. 微星B550M迫击炮,设备管理器 声卡不显示Realtek解决办法
  15. python调用微信截图_Python调用微信截图工具
  16. MATLAB凸优化工具箱CVX用户手册-Chapter5
  17. python菜鸟教程 | print功能
  18. 使用 jodd:form tag
  19. html 各种字符 换位键,excel替换特定位置处的字符
  20. js数组的5种查询方式——find(),findIndex(),indexOf(),lastIndexOf(),include()

热门文章

  1. 转载-程序员编程技术迅速提高的终极攻略
  2. 解读ASP.NET 5 MVC6系列(14):View Component
  3. linux nfs配置
  4. Apache + Tomcat 配置多个应用
  5. 使用 servlet 连接数据库
  6. silverlight之datagrid的一个问题
  7. 为什么每个人都应该尝试Ubuntu下篇 Why Everyone Should Try Ubuntu 分享
  8. eth一张_听说eth2.0利好落地了?那么为何eth反而涨了?
  9. php在window,php在window上的问题
  10. 关于eclipse项目红色感叹号的解决办法