ANR笔记

一、ANR背景

1.1 ANR种类

Service Timeout: 比如前台服务在20s内未执行完成
Broadcast Timeout: 比如前台广播在10s内未执行完成
ContentProvider Timeout: 内容提供者, 在publish超时10s
InputDispatching Timeout: 输入事件分发超时5s, 包括按键和触摸事件

1.2 ANR具体超时

1. Service<ActiveServices.java> {
	1. 前台服务20s int SERVICE_TIMEOUT = 20 * 1000
	2. 后台服务200s int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10
}
2. Broadcast<ActivityManagerService.java> {
	1. 前台广播10s int BROADCAST_FG_TIMEOUT = 10 * 1000
	2. 后台广播60s int BROADCAST_BG_TIMEOUT = 60 * 1000
}
3. InputDispatching<ActivityManagerService.java> {
	1. 输入事件5s int KEY_DISPATCHING_TIMEOUT = 5 * 1000
}

1.3 ANR原因

  • 1、应用在主线程上执行耗时的I/O操作
  • 2、应用在主线程上进行长时间的计算
  • 3、主线程在对另一个进程进行同步binder调用, 而后者需要很长时间才能返回
  • 4、主线程处于阻塞状态, 主动睡眠或者等待其他线程上的同步方法或者同步代码块执行完毕
  • 5、主线程与其他线程之间发生死锁
  • 6、备注: 在Activity.onCreate方法里面调用sleep方法或者说做了耗时操作, 不一定会产生ANR, 其实从ANR本身意为应用程序没有响应, 同时根据总结的ANR的原因可以看出, 耗时操作本身不会产生ANR, 导致ANR的根本还是应用程序无法在一定时间内响应用户的操作, 所以因为主线程被耗时操作占用了, 主线程无法对下一个操作进行响应才会ANR

二、数据收集

ANR日志会写入到/data/anr/trace.txt文件中

三、ANR框架

3.1 ANR-WatchDog

3.1.1 ANR-WatchDog工作流程

在这里插入图片描述

3.1.1 ANR-WatchDog优点

  ANR分析与数据采集与主线程隔离, 避免主线程被阻塞导致ANR分析也受阻. 这一点优于BlockCanary

3.1.2 ANR-Watch缺点

  开启子线程一直运行, 消耗性能

3.2 BlockCanary

3.2.1 BlockCanary原理

在这里插入图片描述

3.2.2 BlockCanary优点

  比较方便的捕捉到卡顿的堆栈

3.2.3 BlockCanary缺点

  无法获取到各个函数的执行耗时, 对于稍微复杂一点的堆栈, 很难找出可能耗时的函数, 也就很难找到卡顿的原因. 而且通过其他线程循环获取主线程的堆栈, 如果稍微处理不及时, 很容易导致获取的堆栈有所偏移, 不够准确, 加上没有耗时信息, 卡顿也就不好定位.

3.3 Matrix-AnrTrace

3.3.1 AnrTrace流程

  • 1、函数执行前后获取当前距离MethodBeat模块初始化的时间offset(为了压缩数据, 存进一个long类型变量中), 并将当前执行的MethodBeat i或者o、method id及时间offset, 存放到一个long类型变量中, 记录到一个预先初始化好的数组long[]中index的位置
  • 2、dispatchMessage执行之前, 重置一个定时器, 如果5s内没有cancel, 则认为发生了ANR, 这时会主动取出当前记录的buffer数据进行独立分析上报, 对这种ANR事件进行单独监控及定位
  • 3、考虑到每个方法执行前后都获取系统时间会对性能影响比较大, 而实际上, 单个函数执行耗时小于5ms的情况, 对卡顿来说不是主要原因, 可以忽略不计. 如果是多次调用的情况, 则在它的父级方法中可以反映出来, 所以为了减少对性能的影响, 通过另一条线程每5ms去更新一个时间变量, 而每个方法执行前后只读取该变量来减少性能损耗
  • 4、堆栈聚类问题: 如果将收集的原始数据进行上报, 数据量很大而且后台很难聚类有问题的堆栈, 所以在上报之前需要对采集的数据进行简单的整合及裁剪, 并分析出一个能代表卡顿堆栈的key, 方便后台聚合
  • 5、通过遍历采集的buffer, 相邻i与o为一次完整函数执行, 计算出一个调用树及每个函数执行耗时, 并对每一级中的相同执行函数做聚合, 最后通过一个简单策略, 分析出主要耗时的那一级函数, 作为代表卡顿堆栈的key.
    在这里插入图片描述

四、卡顿一整套解决方案

  • 1、线下使用Android Studio自带工具Profiler检测卡顿模块的方法耗时、以及CPU使用率情况, 找出卡顿地方对代码进行优化, 例如将IO操作放在子线程中
  • 2、使用自动化工具xCrash捕获ANR日志
  • 3、使用BlockCanary方案设置自定义Printer
  • 4、关键模块方法插桩, 方法执行前后记录方法耗时, 当Printer检测到方法耗时存在时, 从方法耗时集合中截取当前段的方法耗时情况 具体思路是将数组转化为多叉树结构

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值