1. ANR 产生原理
关于 ANR 的触发原因,Android 官方开发者文档中 “What Triggers ANR?” 有介绍,如下:
Generally, the system displays an ANR if an application cannot respond to user input. For example, if an application blocks on some I/O operation (frequently a network access) on the UI thread so the system can't process incoming user input events. Or perhaps the app spends too much time building an elaborate in-memory structure or computing the next move in a game on the UI thread. It's always important to make sure these computations are efficient, but even the most efficient code still takes time to run......
即,常见的有如下两种情况会产生 ANR:
输入事件(例如按键或屏幕轻触事件等)在 5 秒内没有响应;
BroadcastReceiver 在 10 秒内没有执行完成。
结合 Android 相关源码分析可知,输入事件的 ANR 检测是基于输入事件本身驱动的,系统要求在 App进程中处理完成每个输入事件后,通知系统进程事件处理完毕,以此判断 App是否无响应。
要产生 ANR,至少得有两个输入事件,场景如下:
第一个输入事件产生,系统将其发送给用户当前操作的 App;
系统收到第二个事件,发现当前距第一个输入事件发送时间超过 0.5s 仍未处理完毕,则设置一个定时器,5s 后触发;
5s 之后,若系统发现第一个输入事件仍然没有回应时,则触发 ANR,激活 App 中的 Signal Cather 线程生成 traces.txt,然后弹出 ANR 对话框,告知用户 App 无响应。
也就是说,要产生 ANR,第一个输入事件必需在 5.5s 以上没有被处理完成并反馈回系统;并且要有第二个输入事件产生。如果没有第二个输入事件,即便第一个输入事件执行了 60s 或更长时间,也是不会产生 ANR 的。
2. ANR 日志生成原理
系统的 system_server 进程在检测到 App 出现 ANR 后,会向出现 ANR 的进程发送 SIGQUIT (signal 3) 信号。正常情况下,系统的 libart.so 会收到该信号,并调用 Java 虚拟机的 dump 方法生成 traces。
以友盟+的 U-APM 应用性能监控平台为例,集成SDK 后,SDK 会拦截 SIGQUIT。在出现 ANR 时,libcrashsdk.so 会优先收到信号,并生成 traces 和 ANR 日志。在 SDK 处理完信号后,会将信号继续传递给系统的 libart.so,让系统生成 ANR traces.txt。
如下图,红色线为 U-APM SDK 处理 ANR 信号和生成 ANR 日志的流程,紫色线为系统生成 ANR traces.txt 的流程。
U-APM SDK ANR 捕获原理
其中,SDK 生成 traces 时,使用的是 libart.so 中的 dump 方法,生成的内容与系统原生的基本一致。并且,U-APM SDK 在调用 dump 方法时进行了优化,dump 速度较系统生成原生 traces 的速度显著提升,有效地避免了可能因生成 traces 时间过长,而被 system_server 使用 SIGKILL (signal 9) 再次强杀。
在获取所有线程的 traces 信息后,生成完整的 ANR 日志,还会提供获取触发 ANR 的原因、手机中 TOP 进程 CPU 使用率、ANR 进程中 TOP 线程 CPU 使用率、CPU 各核心处理时间分布情况、磁盘 IO 操作等待时长等重要信息。
目前,SDK 生成的 ANR 日志信息,基本包含系统生成的 ANR 日志的所有内容,甚至还包含一些系统日志中没有的内容,以及 App增加的自身的业务相关信息,对分析、定位和解决 ANR 问题,提供了更加强有力的支撑。
3、日志分析
如开发者接入了SDK,ANR 日志将自动启用,出现 ANR 时,会先于系统生成 ANR 日志。日志的主要内容介绍如下:
1). ANR 日志结构
使用日志分析插件,我们可以清晰地看到 生成的 ANR 日志包含的内容以及重点信息,如下:
ANR 日志结构
除了生成的日志以 Section 分为多个部分,其中,包含重要信息的 Section 会使用红色标出,特别重要的信息还会加粗。另外,每个 Section 有快捷键可直接跳转到相应位置。
2). ANR 概要
概要信息如下:
ANR 概要信息
这部分内容主要从系统获取,其包含了 ANR 的进程名、ANR 产生的时间、ANR 的原因、ANR 前后几秒内系统 TOP 进程的 CPU 使用率等。其中,通过 ANR 原因可以得知是输入事件处理超时,还是 BroadcastReceiver 等其它消息处理时间过长;通过 CPU 使用率则可以得知是哪个进程占用 CPU 资源过多。
3). 系统资源使用情况
可记录在出现 ANR 前一段时间内,CPU 平均使用率、CPU 各核心使用率及其耗时分布,ANR 进程中 TOP 线程的执行耗时及比例、出现页错误的次数,磁盘 IO 操作等待时长及次数等内容。如下:
系统资源使用情况
当 IO 繁忙导致 ANR 时,io wait time 和 CPU 时间分布中的 iowait 比例会比较突出;通过 CPU 时间分布中的 user 和 system 占比,则可以知道是用户态代码执行耗时过长,还是 Linux 内核的系统调用耗时太久。
4). ANR traces
traces 信息是 ANR 日志中最关键的内容。如U-APM生成的 traces 信息包含了出现 ANR 时主线程的 native 调用栈和所有线程的 java 调用栈。通常死锁问题通过调用栈中的信息可以很容易发现。
ANR traces
U-APM SDK 的 traces 由 fork 的子进程生成,不会因 Java 虚拟机出现 BUG 导致生成 traces 时又出现 native 崩溃,也不会因 dump 时卡死阻塞整个 ANR 日志的生成。
5). Logcat
以U-APM为例,会在 ANR 时抓取 Android logcat。APM SDK 能绕开部分 ROM 增加的权限控制,拿到当前 App ANR 前相关的 log 信息。当前进程以及当前错误线程输出的 log 会被重点标出,error 和 warning 也会以显目的颜色标出。
logcat
6). 内存等其它信息
通过ANR日志可以分析出一系列的内存信息,如:
系统的 RAM 总内存、剩余可用内存;
当前进程占用的虚拟内存、物理内存;
Java 占用的总内存和可用内存;
Native 占用的内存和可用内存等。
另外,ANR 日志同 Java 和 Native 崩溃日志一样,支持业务自定义日志内容扩展,如:
崩溃前增加简短的自定义头信息;
崩溃前注册外部文件,崩溃时其内容将被带入日志;
崩溃前缓存业务相关的最近若干条操作或信息;
崩溃时通过回调返回业务最新内容等。
4、ANR监控工具
选择一款有超强捕获能力的专业产品,对于开发者定位和修复稳定性问题至关重要。友盟+U-APM SDK集成了UC 内核团队强大的技术及友盟+超强的错误捕获能力,通过数万次捕获实践中积累了丰富经验,在产品、性能和研发能力上都极大保障了开发者定位和修复稳定性问题的超强效率。
点击了解:友盟+U-APM
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。