ANR总结

来源:互联网 发布:学java还是.net 编辑:程序博客网 时间:2024/06/14 21:46

 

一、ANR问题简介

           ANR是Application NotResponding的缩写。在Android系统中,ActivityManagerService(AMS)和WindowManagerService(WMS)会监测应用程序的响应时间,如果应用程序主线程(即UI线程)在规定时间内没有处理输入事件,或者对特定操作没有执行完毕,就会出现ANR。

触发ANR问题的三个直接原因:

1.       主线程没有在5秒内处理完按键、触屏等输入事件

2.       主线程没有在10秒内处理完一个Broadcast

3.      主线程没有在20秒内处理完Service启动的各个生命周期函数

 

           第一种情况系统会弹提示对话框,第二、三种情况不弹对话框,只输出Log。这三种情况是引发ANR问题的直接原因,根本原因需要结合traces信息进行分析,引起ANR问题的根本原因可以大致归纳为以下两类:

       应用进程自身原因,例如

1.      应用进程自身原因,例如

a) 主线程阻塞、挂起、死循环

b) 进程内其他线程CPU占用率高,使主线程无法抢占到CPU时间片

2.      其他应用进程原因,例如:

a) 当前应用进程进行进程间通信请求其他进程,其他进程的操作长时间没有反馈

b) 其他进程CPU占用率高,是当前进程无法抢占到CPU时间片

二、输入事件ANR分析

       以输入事件ANR为例,说明ANR产生的大致流程,首先需要了解Android输入系统,它大致工作原理如下图所示:原始输入事件由EventHub读取,经过InputReader加工,再传给InputDispatcher进行派发。InputDispatcher运行在系统进程(进程名为system_server)的一个单独的线程中,应用程序的主线程在处理事件的过程中,InputDispatcher会不断的检测处理过程是否超时,一旦超时,会通过一系列的回调通知WMS的notifyANR函数,最终会调用到AMS中mHandler对象里的SHOW_NOT_RESPONDING_MSG这个case,此时界面上就显示ANR对话框,同时在logcat中打出ANR相关的信息,接着向应用进程发送一个SIGNAL_QUIT(3)信号,应用进程中的Signal Catcher线程负责处理这个信号,在/data/anr/traces.txt中打印应用进程中所有线程当前的调用栈信息。当前发生ANR的进程会第一个向traces文件中写入信息,反过来说,traces文件中出现的第一个进程正常情况下就是发生ANR的那个进程。

三、ANR Log举例分析

见外面附件。

线程状态主要包括以下这些:

Thread.java中定义的状态

Thread.cpp中定义的状态

说明

TERMINATED

ZOMBIE

线程死亡,终止运行

RUNNABLE

RUNNING/RUNNABLE

线程可运行或正在运行

TIMED_WAITING

TIMED_WAIT

执行了带有超时参数的wait、sleep或join函数

BLOCKED

MONITOR

线程阻塞,等待获取对象锁

WAITING

WAIT

执行了无超时参数的wait函数

NEW

INITIALIZING

新建,正在初始化,为其分配资源

NEW

STARTING

新建,正在启动

RUNNABLE

NATIVE

正在执行JNI本地函数

WAITING

VMWAIT

正在等待VM资源

RUNNABLE

SUSPENDED

线程暂停,通常是由于GC或debug被暂停

 

UNKNOWN

未知状态

四、ANR问题预防与解决

           预防:ANR问题发生在主线程,所以主线程中进行的所有耗时操作都可能引发ANR问题,这些耗时操作包括但不限于:联网、磁盘IO(读写文件,解析json文件,Decode一张图片等)、死锁、死循环、进程间通信(获取系统信息、获取编辑框内容)等。运行在主线程的方法包括:Activity、Service的各生命周期函数;BroadcastReceiver的onReceive方法;Handler的handleMessage方法;输入法各生命周期函数;Touch事件的处理;View的Measure绘制等等,所以不要在这些地方出现耗时的操作。

       解决:把耗时操作放入子线程中,等处理完后通过Handler通知主线程。另外对于子线程要做好线程生命周期的管理,不能new了一个Thread对象,调用其start函数启动后就不再管理了,这样有潜在的“线程泄漏”风险,大量的线程本身就会抢占CPU资源,影响整个应用性能,要在适当的时候(例如Activity销毁时)考虑停止子线程。另外对于部分不便放入子线程的操作(比如获取编辑框内容等)可以考虑通过减少调用次数,来达到降低ANR概率的目的。