Android Anr 原理分析

最近做了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

 

Android Anr 原理分析_第1张图片

 

Input ANR

广播与contentprovide的ANR与Service大同小异,这里不再讲述了。Input 的ANR与这些都有点不同,同时是最复杂的

Input的ANR的策略主要是在InputDispatcher里进行的,可以看

frameworks/native/services/inputflinger/InputDispatcher.cpp

Android Anr 原理分析_第2张图片

大家可以看到InputDispatcher是在native层的,它也是在system_server进程中,工作在InputDispatcherThread线程中,InputDispatcher主要进行按键事件的派发,所以正适合进行ANR的调度

简单的说InputDispatcher 内部有个threadLoop,会不断循环执行dispatchOnce()来执行按键事件或者一些上一次循环存在队列里面的命令,当没有事件时,线程会在等待状态,等待事件唤醒或者根据给定的时间唤醒

一次正常的按键事件,需要找到应用的窗口findFocusedWindowTargetsLocked(),向窗口派发按键,在找到窗口以后,会调用checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发,它们就是是 ANR reason

  1. //当窗口暂停的情况,则保持等待
    if(windowHandle->getInfo()->paused)
  2.  //当窗口连接未注册,则保持等待
      ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());    
    if (connectionIndex < 0) 
    
  3.   //当窗口连接已死亡,则保持等待
        sp connection = mConnectionsByFd.valueAt(connectionIndex);
        if (connection->status != Connection::STATUS_NORMAL) {
  4.  // 当窗口连接阻塞,则保持等待
        if (connection->inputPublisherBlocked) 
  5.    //确保分发队列,并没有存储过多事件
     if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) 

我们来看ANR具体怎么发生的

我们首先要明确两点

  • 按键正常情况下是成对的,有keydown,keyup,在InputDispatcher里面对一次按键操作是需要派发两次事件,即按下与松开
  • Android 默认情况下对没有模拟按键事件的输入设备的按键事件会进行模拟按键,简而言之就是当按键长按安卓会模拟多次按键按下事件,以实现重复按键,当按键按下0.5s进行第一次模拟按键,以后没0.05s进行一次repeat,注意模拟按键只针对按下的事件

 我们来看正常的按键流程

Android Anr 原理分析_第3张图片

按键事件在通过checkWindowReadyForMoreInputLocked() 的拦截后事件添加到outboundQuque,当事件派发到应用,outboundQuque删除事件,同时事件加入到等待队列waitQueue中,当应用处理完事件,又经过层层调用到InputDispatcher,事件从waitQueue中删除,这就是一次完整的按键事件

来看一种ANR情况

Android Anr 原理分析_第4张图片

按键派发到checkWindowReadyForMoreInputLocked() 被窗口没有焦点,或者窗口暂停,或者不能与窗口通信这几种可能的原因阻塞了,执行到该步骤,记录超时时间是T1+5s,线程唤醒时间是T1+5s,等到T1+5s线程唤醒,执行onANR(),最终执行AMS的appNotResponding()

 

Android Anr 原理分析_第5张图片

按键在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的逻辑

 

 

 

你可能感兴趣的:(android,ANR,安卓)