文章目录
前言
ANR问题在我们Android开发中经常会遇到,但是如何清晰地把ANR问题的定义表述出来和详述ANR问题如何解决则是一个需要整理的过程,本节就针对ANR问题进行整理
一、ANR是什么?
**ANR(Application Not responding)**是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。ANR由消息处理机制保证,Android在系统层实现了一套精密的机制来发现ANR,核心原理是消息调度和超时处理。
ANR的实质是主线程失去在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入)的能力,前置条件有新的操作需要主线程进行响应,而造成主线程失去响应能力的往往是一些主线程中的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局,都会降低应用程序的响应能力。
二、ANR时间规定
1.Service Timeout
- 对于前台服务,定义的超时时间为20秒
- 对于后台服务,定义的超时时间为200秒
2.BroadcastQueue Timeout
- 对于前台广播,定义的超时时间为10秒
- 对于后台广播,定义的超时时间为60秒
3.ContentProvider Timeout
- 超时时间为10秒
4.InputDispatching Timeout
- 输入事件分发超时5s,包括按键和触摸事件
注意事项:Input的超时机制与其他机制不同,对于input来说即使某次事件执行事件超过timeout时长,只要用户后续没有再生成输入事件,则不会触发ANR
三、ANR场景分析
解决ANR一直是Android 开发者需要掌握的重要技巧,一般从三个方面着手。
开发阶段:通过工具检查各个方法的耗时,卡顿情况,发现一处修改一处。
线上阶段:这个阶段主要依靠监控工具发现ANR并上报,比如matrix。
分析阶段:如果线上用户发生ANR,并且你获取了一份日志,这就涉及了本文要分享的内容——ANR日志分析技巧。
InputEvent Timeout
a.InputDispatcher发送key事件给 对应的进程的 Focused Window,对应的window不存在、处于暂停态、或通道(input channel)占满、通道未注册、通道异常、或5s内没有处理完一个事件,就会发生ANR
b.InputDispatcher发送MotionEvent事件有个例外之处:当对应Touched Window的 input waitQueue中有超过0.5s的事件,inputDispatcher会暂停该事件,并等待5s,如果仍旧没有收到window的‘finish’事件,则触发ANR
c.下一个事件到达,发现有一个超时事件才会触发ANR
BroadcastReceiver Timeout
a.静态注册的广播和有序广播会ANR,动态注册的非有序广播并不会ANR
b.广播发送时,会判断该进程是否存在,不存在则创建,创建进程的耗时也算在超时时间里
c.只有当进程存在前台显示的Activity才会弹出ANR对话框,否则会直接杀掉当前进程
d.当onReceive执行超过阈值(前台15s,后台60s),将产生ANR
e.如何发送前台广播:Intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
Service Timeout
a.Service的以下方法都会触发ANR:onCreate(),onStartCommand(), onStart(), onBind(), onRebind(), onTaskRemoved(), onUnbind(), onDestroy().
b.前台Service超时时间为20s,后台Service超时时间为200s
c.如何区分前台、后台执行————当前APP处于用户态,此时执行的Service则为前台执行。
d.用户态:有前台activity、有前台广播在执行、有foreground service执行
ContentProvider 类型
a.ContentProvider创建发布超时并不会ANR
b.使用ContentProviderclient来访问ContentProverder可以自主选择触发ANR,超时时间自己定
client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
四.导致ANR的原因
导致ANR的原因大致分为两类,一种是应用层导致ANR,一种是系统导致ANR。
应用层导致ANR
a. 函数阻塞:如死循环、主线程IO、处理大数据
b. 锁出错:主线程等待子线程的锁
c.内存紧张:系统分配给一个应用的内存是有上限的,长期处于内存紧张,会导致频繁内存交换,进而导致应用的一些操作超时
系统导致ANR
a. CPU被抢占:一般来说,前台在玩游戏,可能会导致你的后台广播被抢占CPU
b.系统服务无法及时响应:比如获取系统联系人等,系统的服务都是Binder机制,服务能力也是有限的,有可能系统服务长时间不响应导致ANR
c. 其他应用占用的大量内存
应用层导致的ANR往往在开发阶段便可以通过相关日志定位语句分析得出,但系统导致的ANR往往需要通过分析日志观察CPU使用情况
五.ANR日志分析
发生ANR时,如果是开发阶段,可以直接通过编译器打印日志收集进行观察,也可以通过观察trace文件得到我们想要的信息
系统产生的anr日志文件(手机的**/data/anr** 目录下,文件名称可能各厂商不一样,业内大多称呼为trace文件),内含如下几项重要信息。
CPU负载分析
来看一段ANR日志中关于CPU的分析
Load: 2.62 / 2.55 / 2.25
CPU usage from 0ms to 1987ms later (2020-03-10 08:31:55.169 to 2020-03-10 08:32:17.156):
41% 2080/system_server: 28% user + 12% kernel / faults: 76445 minor 180 major
26% 9378/com.xiaomi.store: 20% user + 6.8% kernel / faults: 68408 minor 68 major
........省略N行.....
66% TOTAL: 20% user + 15% kernel + 28% iowait + 0.7% irq + 0.7% softirq
第一行:1、5、15 分钟内正在使用和等待使用CPU 的活动进程的平均数
第二行:表明负载信息抓取在ANR发生之后的0~1987ms。同时也指明了ANR的时间点:2020-03-10 08:31:55.169
中间部分:各个进程占用的CPU的详细情况
最后一行:各个进程合计占用的CPU信息。
对最后一行分析如下:
1.iowait占比非常高,说明输入输出流堵塞, 一般这种情况发生在读写操作、网络操作
2.user占比非常高,说明在进行大量的计算,导致Ui主线程分配不到时间片,从而导致ANR的产生
3.kernel占比非常高,说明系统本身内核层就存在严重的Bug
4.softirq为软中断,irq为硬中断
内存信息
Total number of allocations 476778 进程创建到现在一共创建了多少对象
Total bytes allocated 52MB 进程创建到现在一共申请了多少内存
Total bytes freed 52MB 进程创建到现在一共释放了多少内存
Free memory 777KB 不扩展堆的情况下可用的内存
Free memory until GC 777KB GC前的可用内存
Free memory until OOME 383MB OOM之前的可用内存
Total memory 当前总内存(已用+可用)
Max memory 384MB 进程最多能申请的内存
这里来看一段内存使用信息状况
**Free memory until OOME **的值很小的时候,已经处于内存紧张状态。应用可能是占用了过多内存。
堆栈消息
观察堆栈消息,主要是分析堆栈中各线程的状态,
例
main线程处于 BLOCK、WAITING、TIMEWAITING状态,那基本上是函数阻塞导致ANR;
如果main线程无异常,则应该排查CPU负载和内存环境。