ANR机制要点总结

SNR(System Not Respoding),SNR反映的问题是系统进程(system_server)失去了响应能力,SNR由Watchdog机制保证.
ANR由消息处理机制保证,

一、Service

Service运行在应用程序的主线程,如果Service的执行时间超过20秒,则会引发ANR。
当发生Service ANR时,一般可以先排查一下在Service的生命周期函数中(onCreate(), onStartCommand()等)有没有做耗时的操作,譬如复杂的运算、IO操作等。

1、Service超时是如何检测的?
Android是通过设置定时消息实现的。定时消息是由AMS的消息队列处理的(system_server的ActivityManager线程)。

2、Service ANR机制:

  1. 当Service的生命周期开始时,通过AMS.MainHandler抛出一个定时消息 SERVICE_TIMEOUT_MSG。
    前台进程中执行Service,超时时间是SERVICE_TIMEOUT(20秒))
    后台进程中执行Service,超时时间是SERVICE_BACKGROUND_TIMEOUT(200秒)

  2. 当Service的生命周期结束时,会清除 SERVICE_TIMEOUT_MSG。

  3. 如果在超时时间内,SERVICE_TIMEOUT_MSG没有被清除,经过一些判定后,决定要报告ANR,最终调用AMS.appNotResponding()方法。

二、BroadcastReceiver

BroadcastReceiver.onReceive() 执行时间不能超过10秒,否则,就会引发ANR。

AMS维护了两个广播队列BroadcastQueue:

  • foreground queue,前台队列的超时时间是10秒
  • background queue,后台队列的超时时间是60秒

所有发送的广播都会进入到队列中等待调度,在发送广播时,可以通过Intent.FLAG_RECEIVER_FOREGROUND参数将广播投递到前台队列。 AMS线程会不断地从队列中取出广播消息派发到各个接收器(BroadcastReceiver)。

处理消息的顺序:
1、处理“并行广播消息”
2、处理阻塞的广播消息
3、处理“串行广播消息”

ANR监测机制只在“串行广播消息”中才发挥作用,也就是说“并行广播消息”是不会发生ANR的。

4、从队列中取出一条“串行广播消息”,然后设置一个定时消息BROADCAST_TIMEOUT_MSG来跟踪当前广播消息的执行情况。
5、如果所有的接收器都处理完毕了,则会清除该消息。
6、设置完定时消息后,就开始派发广播消息。

ANR机制:
1、AMS维护着广播队列BroadcastQueue,AMS线程不断从队列中取出消息进行调度,完成广播消息的派发。
2、在派发“串行广播消息”时,会抛出一个定时消息BROADCAST_TIMEOUT_MSG,在广播接收器处理完毕后,AMS会将定时消息清除。
3、如果BROADCAST_TIMEOUT_MSG得到了响应,就会判断是否广播消息处理超时,最终通知ANR的发生。

三、Input

应用程序可以接收输入事件(按键、触屏、轨迹球等),当5秒内没有处理完毕时,则会引发ANR。

Android有一套输入子系统来发现各种输入事件, 这些事件最终都会被InputDispatcher分发到各个需要接收事件的窗口。 那么,窗口如何告之InputDispatcher自己需要处理输入事件呢?Android通过InputChannel 连接InputDispatcher和窗口,InputChannel其实是封装后的Linux管道(Pipe)。 每一个窗口都会有一个独立的InputChannel,窗口需要将这个InputChannel注册到InputDispatcher中。

  • InputDispatcherThread是一个线程,它处理一次消息的派发
  • 输入事件作为一个消息,需要排队等待派发,每一个Connection都维护两个队列:
    outboundQueue: 等待发送给窗口的事件。每一个新消息到来,都会先进入到此队列
    waitQueue: 已经发送给窗口的事件
  • publishKeyEvent完成后,表示事件已经派发了,就将事件从outboundQueue挪到了waitQueue

一个正常的输入事件会经过从outboundQueue挪到waitQueue的过程,表示消息已经派发出去;再经过从waitQueue中移除的过程,表示消息已经被窗口接收。 InputDispatcher作为中枢,不停地在递送着输入事件,当一个事件无法得到处理的时候,InputDispatcher不能就此死掉啊,否则系统也太容易崩溃了。 InputDispatcher的策略是放弃掉处理不过来的事件,并发出通知(这个通知机制就是ANR),继续进行下一轮消息的处理。

在派发事件时,需要找到当前的焦点窗口,焦点窗口才是最终接收事件的地方,找窗口的过程就会判断是否已经发生了ANR。
1、首先寻找接收输入事件的窗口。
2、在找到窗口以后,会检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发:
—— 场景1: 窗口处于paused状态,不能处理输入事件
—— 场景2: 窗口还未向InputDispatcher注册,无法将事件派发到窗口
—— 场景3: 窗口和InputDispatcher的连接已经中断,即InputChannel不能正常工作
—— 场景4: InputChannel已经饱和,不能再处理新的事件
—— 场景5: 对于按键类型(KeyEvent)的输入事件,需要等待上一个事件处理完毕
—— 场景6: 对于触摸类型(TouchEvent)的输入事件,可以立即派发到当前的窗口,因为TouchEvent都是发生在用户当前可见的窗口。但有一种情况, 如果当前应用由于队列有太多的输入事件等待派发,导致发生了ANR,那TouchEvent事件就需要排队等待派发。
3、然后,上述有任何一个场景发生了,则输入事件需要继续等待,紧接着就会判断是不是已经的等待超时了。
4、最后,如果当前事件派发已经超时,则说明已经检测到了ANR。

在InputDispatcher派发输入事件时,会寻找接收事件的窗口,如果无法正常派发,则可能会导致当前需要派发的事件超时(默认是5秒)。 Native层发现超时了,会通知Java层,Java层经过一些处理后,会反馈给Native层,是继续等待还是丢弃当前派发的事件。

四、小结

ANR监测机制包含三种:

  • Service ANR,前台进程中Service生命周期不能超过20秒,后台进程中Service的生命周期不能超过200秒。 在启动Service时,抛出定时消息SERVICE_TIMEOUT_MSG或SERVICE_BACKGOURND_TIMEOUT_MSG,如果定时消息响应了,则说明发生了ANR
  • Broadcast ANR,前台的“串行广播消息”必须在10秒内处理完毕,后台的“串行广播消息”必须在60秒处理完毕, 每派发串行广播消息到一个接收器时,都会抛出一个定时消息BROADCAST_TIMEOUT_MSG,如果定时消息响应,则判断是否广播消息处理超时,超时就说明发生了ANR
  • Input ANR,输入事件必须在5秒内处理完毕。在派发一个输入事件时,会判断当前输入事件是否需要等待,如果需要等待,则判断是否等待已经超时,超时就说明发生了ANR

从ANR的三种监测机制中,我们看到不同超时机制的设计:

Service和Broadcast都是由AMS调度,利用Handler和Looper,设计了一个TIMEOUT消息交由AMS线程来处理,整个超时机制的实现都是在Java层。
InputEvent由InputDispatcher调度,待处理的输入事件都会进入队列中等待,设计了一个等待超时的判断,超时机制的实现在Native层。

Thanks:
ANR机制以及问题分析

猜你喜欢

转载自blog.csdn.net/iluojie/article/details/80753731
ANR