最近做了ANR的一些研究,讲述自己对ANR流程的一些总结,本文主要从ANR流程上来看不分析细节,需要细节的推荐
https://duanqz.github.io/2015-10-12-ANR-Analysis#213-input%E5%A4%84%E7%90%86%E8%B6%85%E6%97%B6
非常全面.
ANRgoogle官方有比较清楚的解释,同时指明了ANR发生在主线程,这也隐含表示了ANR是发生在应用内部的,而不是在系统中,所以我们的system_server 比较适合进行ANR的调度,所以我们的ANR都是在system_server监控与执行的
Service ANR
Service的 ANR 其实就是在system_server里远程调用应用service的每个生命周期函数前发送一个延迟时间是20s或者200s(由应用service的前后台决定)的消息SERVICE_TIMEOUT_MSG,service的生命周期函数比如onCreate()在延迟消息时间到达前调用结束就可以远程调用system_server remove()延迟消息,这样就取消了ANR了.
如果service的生命周期函数执行时间超过20s或者200s,SERVICE_TIMEOUT_MSG的消息得到执行调用serviceTimeout() 最终执行AMS里的appNotResponding()方法来执行ANR
Input ANR
广播与contentprovide的ANR与Service大同小异,这里不再讲述了。Input 的ANR与这些都有点不同,同时是最复杂的
Input的ANR的策略主要是在InputDispatcher里进行的,可以看
frameworks/native/services/inputflinger/InputDispatcher.cpp
大家可以看到InputDispatcher是在native层的,它也是在system_server进程中,工作在InputDispatcherThread线程中,InputDispatcher主要进行按键事件的派发,所以正适合进行ANR的调度
简单的说InputDispatcher 内部有个threadLoop,会不断循环执行dispatchOnce()来执行按键事件或者一些上一次循环存在队列里面的命令,当没有事件时,线程会在等待状态,等待事件唤醒或者根据给定的时间唤醒
一次正常的按键事件,需要找到应用的窗口findFocusedWindowTargetsLocked(),向窗口派发按键,在找到窗口以后,会调用checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发,它们就是是 ANR reason
//当窗口暂停的情况,则保持等待 if(windowHandle->getInfo()->paused)
//当窗口连接未注册,则保持等待 ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel()); if (connectionIndex < 0)
//当窗口连接已死亡,则保持等待 spconnection = mConnectionsByFd.valueAt(connectionIndex); if (connection->status != Connection::STATUS_NORMAL) {
// 当窗口连接阻塞,则保持等待 if (connection->inputPublisherBlocked)
//确保分发队列,并没有存储过多事件 if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty())
我们来看ANR具体怎么发生的
我们首先要明确两点
我们来看正常的按键流程
按键事件在通过checkWindowReadyForMoreInputLocked() 的拦截后事件添加到outboundQuque,当事件派发到应用,outboundQuque删除事件,同时事件加入到等待队列waitQueue中,当应用处理完事件,又经过层层调用到InputDispatcher,事件从waitQueue中删除,这就是一次完整的按键事件
来看一种ANR情况
按键派发到checkWindowReadyForMoreInputLocked() 被窗口没有焦点,或者窗口暂停,或者不能与窗口通信这几种可能的原因阻塞了,执行到该步骤,记录超时时间是T1+5s,线程唤醒时间是T1+5s,等到T1+5s线程唤醒,执行onANR(),最终执行AMS的appNotResponding()
按键在T1按下通过checkWindowReadyForMoreInputLocked() 的拦截派发到应用的窗口,这时outbondqueue.size =0
waitQueue.size = 1,应用进程卡住当按键keyup事件到来前都没有处理按键事件,所以当keyup事件checkWindowReadyForMoreInputLocked() 时,被outboundQueue.isEmpty()|| waitQueue.isEmpty() 拦截,记录当前等待事件T2+5s,唤醒时间T2+5s,当T2+5s到达,执行ANR
从上面流程看出ANR在地二次事件才计算时间,那对于长按按键呢,这时候只有按下事件,发生ANR吗,事实时也有ANR发生,原因是当长按时0.5s后系统模拟了第二次的按下事件,该事件与正常事件流程一致,如果应用窗口进程卡住没有处理事件,也导致ANR
我们再看看如果是KeyUP事件呢,如果在KeyUp事件派发的时候应用窗口进程卡住不处理,有ANR吗
从以上分析可看出没有事件的情况下没有ANR发生的条件,在6.0系统实验也的确不造成ANR,大家可以自己试试,只有当下次按键事件过来才执行5s后ANR的逻辑