06.Android ANR详解

前言

ANR全称:Application Not Responding,也就是应用程序无响应。

1.原因

Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。

以下四个条件都可以造成ANR发生:

  • InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
  • BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
  • Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
  • ContentProvider Timeout :ContentProvider的publish在10s内没进行完。

各组件触发ANR的过程

会产生ANR的,包括Activity、Service、BroadCastReceiver、ContentProvider和Application,他们各自的ANR过程都有些不同,我们先从简单的看起。

1.BroadCastReceiver

时间定义

广播的超时时间是定义在AMS里:

BROADCAST_FG_TIMEOUT:10s

BROADCAST_BG_TIMEOUT:60s

前/后台广播是在发送Intent时,在intent.addFlag里定义的。

触发时机

当AMS处理广播时,会调用processNextBroadcast函数,这里面会处理并行广播和串行广播,其中,并行广播是单向通知,不需要等待反馈,所以并行广播没有ANR。

在处理串行广播时:

首先,判断是否已经有一个广播超时消息;然后,根据目标进程优先级,分别在前台队列和后台队列(超时时限不同)中排队处理;接下来,根据不同的队列,发出不同延时的ANR消息;如果处理及时,取消延时消息;如果处理超时,触发ANR;

ANR处理

广播的ANR处理相对简单,主要是再次判断是否超时、记录日志,记录ANR次数等。

然后就继续调用processNextBroadcast函数,处理下一条广播了。

2.Service

Service真正的管理者是ActiveServices,AMS虽然会去交互与通信,但在启动服务时,是交给ActiveServices去做的。

时间定义

服务的超时时间是定义在AS里:

SERVICE_TIMEOUT:20s;

SERVICE_BACKGROUND_TIMEOUT:200s;

在ActiveService执行startServiceLocked启动服务时,会判断启动服务的发起方的进程(Process.THREAD_GROUP_BG_NONINTERACTIVE),以便选择不同的超时时间。

触发时机

ActivityServices会调用realStartServiceLocked函数启动Service,最前面会先发送一个延迟消息,sendMessageAtTime(msg,time);其中的time,是在最开始startServiceLocked函数中判断出前/后台进程,然后装在ServiceRecord中,一路传过来的。

如果Service操作执行完毕,会执行serviceDoneExecutingLocked,这里面会移除延迟消息。

如果Service执行超时,会执行mServices.serviceTimeout。

ANR处理

其实Service的ANR处理也相对简单,记录日志,清理anr活动等。

mServices.serviceTimeout((ProcessRecord)msg.obj)函数里,提供了进程信息。

3.ContentProvider

ContentProvider也是会ANR的,如果AMS中的ContentProviderClient在处理中超时,也可以启动ANR,超时时间和是否使用,由开发者决定:

CONTENT_PROVIDER_PUBLISH_TIMEOUT:10s

CONTENT_PROVIDER_RETAIN_TIME:20s

4.Application

Application的启动是执行在主线程的,attachBaseContext和onCreate等回调也是在主线程的,这里如果出现ANR,会影响到当前组件的运行。

5.Activity

Activity的ANR是相对最复杂的,也只有Activity中出现的ANR会弹出ANR提示框。

InputDispatching

Activity最主要的功能之一是交互,为了方便交互,Android中的InputDispatcher会发出操作事件,最终在Input Manager Service中发出事件,通过InputChannel,向Activity分发事件。

交互事件必须得到响应,如果不能及时处理,IMS就会报出ANR,交给AMS去弹出ANR提示框。

KeyDispatching

如果输入是个Key事件,会从IMS进入ActivityRecord.Token.keyDispatchingTimeOut,然后进入AMS处理,不同的是,在ActivityRecord中,会先截留一次Key的不响应,只有当Key连续第二次处理超时,才会弹出ANR提示框。

窗口焦点

Activity总是需要有一个当前窗口来响应事件的,但如果迟迟没有当前窗口(获得焦点),比如在Activity切换时,旧Activity已经onPause,新的Activity一直没有onResume,持续超过5秒,就会ANR。

App的生命周期太慢,或CPU资源不足,或WMS异常,都可能导致窗口焦点。

时间定义

在AMS中定义

KEY_DISPATCHING_TIMEOUT:5s

INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT :60s

触发时机

输入界面的触发时机,绝不是尽快提示ANR,而是尽量不提示ANR,因为ANR一旦提示出来,App一般也就关掉了。

对于InputDispatcher来说,如果有新的输入事件时,上一个输入事件还没有处理完,才会通知IMS去判断,是否需要处理ANR。

ANR处理

首先,写日志(data/anr/traces.txt)。

然后,会发出一个Message,弹出ANR提示框。

2.ANR的处理和日志

ANR是在AMS的appNotResponding函数中处理的,主要是记录日志,和弹出提示。

如何记录

log日志记录在data/anr/traces.txt文件中,这个文件每次只记录最近的一次ANR,有可能记录失败。

文件内容包括dump栈,CPU负载,IO Wait等

如何解读

分析ANR,除了检查代码的生命周期函数是否有耗时操作,还可以分析traces日志,分析角度主要包括:

  • 栈信息,一般可以知道在哪段代码附近发生了ANR,可能不是直接原因,但一般在问题点附近。
  • CPU用量,看负载比例和平均负载,判断是不是有别的App占用了过多的CPU。
  • IO Wait,看IOWait的占比是否很高,判断是否在等待IO。

3.ANR的原因

如果从根源上划分的话,导致ANR的原因有如下几点:

  • IO操作,如数据库、文件、网络
  • CPU不足,一般是别的App占用了大量的CPU,导致App无法及时处理
  • 硬件操作,如camera
  • 线程问题,如主线程被join/sleep,或wait锁等导致超时
  • service问题,如service忙导致超时无响应,或service binder的数量达到上限
  • system server问题,如WatchDog发现ANR
少年与爱永不老去,即便披荆斩棘,丢失怒马鲜衣。
posted @   契阔  阅读(414)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示