Android ANR问题基本分析方法

对于ANR问题的介绍,首先可以参考google的官方文档
ANR

一、ANR的种类

ANR的问题表现为以下4种:

  • 输入调度超时:如果您的应用在 5 秒内未响应输入事件(例如按键或屏幕触摸)。
    日志的标志性打印为:
//输入事件未响应的日志打印
Reason: Input dispatching timed out ([Gesture Monitor] edge-swipe (server) is not responding. Waited 5008ms for MotionEvent(deviceId=13, eventTime=54754402000,.....
  • 执行服务:如果应用声明的服务无法在几秒内完成 Service.onCreate() 和 Service.onStartCommand()/Service.onBind() 执行。对于前台服务来说,这个时间是20s,后台服务是200s
//执行服务超时的打印,以下为执行systemui中的服务超时了
Reason: executing service com.android.systemui/.SystemUIService
  • 未调用 Service.startForeground():如果您的应用使用 Context.startForegroundService() 在前台启动新服务,但该服务在 5 秒内未调用 startForeground()。
//日志的打印如下
ActivityManager: ANR in com.example.testaidl4
ActivityManager: PID: 8076
ActivityManager: Reason: Context.startForegroundService() did not then call Service.startForeground()
  • intent 广播:如果 BroadcastReceiver 在设定的一段时间内没有执行完毕。则可能出现ANR。
    根据发送广播sendBroadcast(Intent intent)中的intent的flags是否包含FLAG_RECEIVER_FOREGROUND来决定把该广播是放入前台广播队列或者后台广播队列,前台广播队列的超时为10s,后台广播队列的超时为60s。默认情况下广播是放入后台广播队列。
//广播ANR的日志如下
ActivityManager: ANR in com.example.testaidl4:broadcast
ActivityManager: PID: 5326
ActivityManager: Reason: Broadcast of Intent ...

日志中打印的ANR原因为以上4种,但是根因则多种多样,需要具体分析。

二、ANR的出现原因

ANR细分可以分为如下的根因:

  1. activity的主线程中执行耗时任务致使输入事件得不到响应(若用户没有输入任何事件,即使主线程阻塞了,也不会ANR,)。
  2. service的onCreate或者onStartCommand执行耗时任务导致执行时间过长。
  3. Broadcast接收到广播后在主线程执行时间过长。
  4. 主线程执行时出现死锁,导致无法执行下去,线程卡住。
  5. 在进行Binder调用时,被Binder对端Block。
  6. 进行系统调用时,由于Binder被占满导致主线程无法和SystemServer通信,进而主线程卡住。
  7. CPU满负荷,主线程长时间得不到CPU的调度,导致无响应。
  8. GC回收内存导致线程等待,从而anr。

1、2、3、4都比较容易定位解决,5、6、7、8往往不容易分析。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

三、ANR的基本定位方法

1.定位手段

1、查看applogcat,可以pull日志文件或者直接实时取日志。在日志文件中搜索"ANR in",会直接显示出anr出现的直接原因。例如:
请添加图片描述
根据anr的原因,再进行针对性的分析。

2、查看trace文件,当出现anr时,系统会在终端的/data/anr/目录下生成trace文件,我们将这些文件pull出来进行分析。
trace文件会打印出出现anr时anr进程的调用栈,我们通过调用栈可以判断是在哪个方法中卡住了,也会打印出持锁以及等待锁的情况,因此也常用于分析死锁问题。

终端上弹ANR对话框的时间并不是准确的ANR发生时间,往往比发生时间要晚一些。ANR in打印的时间也不是ANR的发生时间。要知道ANR发生的准确时间,还是需要从trace日志里面去看。

2.排查方法

前面介绍ANR出现的原因有几种,因此通过排查日志或者代码看能匹配哪种ANR的特性。一般可以沿用如下顺序:
1、在获取到ANR发生的直接原因后,首先进行代码分析,排查是否有在activity、service或者broadcast的主线程执行耗时任务的情况,必要时可以添加日志来看执行时长。

2、根据trace日志中ANR发生的时间来分析日志前后,看是否有什么异常发生。

3、查看trace日志,看是否有死锁的发生。查看trace文件中的"main" prio=…,看是否有waiting to lock(等待锁)或者locked(持锁)的发生,并查找持锁、等待锁的链,来看是否出现了死锁问题。如下图,Thread-1和Thread-0分别持对方等待的锁,都不释放,这便出现了死锁的问题。
请添加图片描述
4、看是否被Binder 对端block,trace中类似于如下的打印,从调用栈来看,明显是进行系统调用,transact进行binder调用时。

请添加图片描述
Binder通信的两端方法名是一致的, 所以可以在当前日志搜索getNetworkPolicies,找到对端:
请添加图片描述
在等待Tid为35的线程的一个持锁。
请添加图片描述

5、查看Binder是否被占满
系统对每个process最多分配15个binder线程,如果另一个process发送太多重复binder请求,那么就会导致接收端binder线程被占满,从而处理不了其它的binder请求。这时候请求端发起的请求就会阻塞等待了(未设置异步请求的前提下),这本身就是系统的一个限制,如果应用未按照系统的要求来实现对应逻辑,那么就会造成问题。
判断Binder是否用完,可以在trace中搜索关键字"binder_f",如果搜索到则表示已经用完。

6、查看CPU、IO情况
在applogcat中,在ANR in下面,会打印出问题发生时CPU的使用情况。
如下面这种,io占用了CPU 87%,那么可能就是做了大量的IO操作导致。
请添加图片描述

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值