ANR 原理与实战技巧

首先,每天看到不断有新人关注着这个公众号,心情很是愉悦。一种认可,一种信任,也是我前进的动力。感谢大家的支持与鼓励。     如果你喜欢这篇文章,或者喜欢这个图片,或者有所感悟,或者仅仅因为喜欢代码GG,分享此文到技术群里,以便此文的价值最大化。    再次感谢大家的鼎力支持,抱拳拱手,难表胜意。俯首向前,继续码文,聊表心意。

00     手机用用,就卡卡卡。莫名其妙的出现一堆程序无响应,欲哭无泪。这是为什么呢?因为你用的android手机。     android手机,为了让手机卡的不成样子,还想让用户知道,就发明了ANR弹框。弹框就弹框,一般的继续等待都是无果,只有结束之才能解决。就像电脑卡死之后,任务管理器启动不起来,想禁止某个进程,徒劳无返。今天我们来唠唠嗑,看看ANR到底是 何方妖怪。 01     ANR:Application Not Responding,即应用无响应 。简洁有力,直奔主题,不做过多解释。怎么产生的呢?     android设计了一种机制,认为一些阻挡它生命周期的返回,不能无限制下去。比如一个点击触屏动作,android系统就计个时,希望你5s内完成动作,如果你5s还没返回,android系统就会认为你傻了,处理这么久还不返回,android系统就干脆弹个框个,给用户说下,这个过程太长了,你等不等,你最好不要等,把它干掉。android默认写入的按键超时配置为: static final int KEY_DISPATCHING_TIMEOUT = 5*1000

系统都设计了哪些ANR: 1:KeyDispatchTimeout(5 seconds) --主要类型 按键或触摸事件在特定时间内无响应 2:BroadcastTimeout(10 seconds) BroadcastReceiver 在特定时间内无法处理完成 3:ServiceTimeout(20 seconds)  Service 在特定的时间内无法处理完成 除此之外,还有 ContentProvider,只是一般很少见。

广播和服务,在后台启动的时候,时间会是 60s,于是我们在分析问 题时候,尽量将 anr 的 log 分析,将查看的 log 从发生 anr 的时刻向 前找 1 分钟。 02 UI线程(主线程)就干简单轻量的事情,主要维护和system_server的通信交互,耗时的就交给其他线程(new Thread 或者AsyncTask),如果你干了比较耗时的事情,从而导致system_server跟你聊天的时候,你不在线,那么你就危险了,system_server里面就开始给你倒计时了,时间一到,它就认为你可以去 go to hell,然后你就出局了。所以,各司其职,主线程主要搞好和系统的关系,系统是个急性子,没事就弹框搞掉你,有点监工的意思。 那么哪些算UI主线程呢? Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc Mainthread handler: handleMessage(), post*(runnable r), etc Other

耗时的工作(比如数据库操作,I/O,连接网络或者别的有 可能阻碍 UI 线程的操作)把它放入单独的线程处理. 比如打开wifi(因为跨进程操作,有可能wifiserver那边处理超时) 读写文件(操作是个iowait负载较大的行为,很容易anr) 查询语句(在数据库内容暴增之后,出现严重的性能问题,产生anr) SharedPreferences 的commit操作,本身是个等待操作,在我们activity退出时,有时保存当前状态,方便恢复,会使用commit,如果我们也有一个此时在操作,因为这个操作是有个锁,引起anr list的排序。(算法的质量,以及当列表数目激增后,是否能快速算完,是个耗时操作,会产生anr) bitmap的运算,(旋转,特效处理等) ThreadPoolExecutor 线程池,当我们从这里获取一个线程时候,如果此时所有线程都被使用,就只能迫使等待,此时会出现anr

扯了这么多犊子,我们继续扯。。 03 出现anr的时候,如何定位,分析问题呢? 1:查看 bug 上面的描述信息,看下堆栈,cpu 使用情况。 首先我们要确定的是否此 log 有效。 确认依据:看 bug 的描述 看 bug 提供的描述信息,堆栈异常是否和标题一致。 ● 如果不一致,此问题直接给出分析结果,转出对应模块负责。 ● 如果一致,我们需要去看 trace 文件,查看里面的出现的栈信息是否和描述的一致。(通过看测试贴出的 anr 栈里面的时间信息,和我们的 trace 的时间是否一致,一致,此份 trace 有效) ● 如果不一致,我们需要去看 log,搜索 am_anr,看下是否在测试贴出的 anr 栈的时间信息处,是否发生了 anr,如果有,此份 log有效,可以进行分析。 ● 如果我们看到栈信息,去看对应代码,发现此处是个跨进程调用,循环调用,查询语句,那么出现 anr 的原因,可以去怀疑这里耗时,等待。 ● 如果是跨进程调用,那么需要看下对应进程的堆栈,看下请求是否响应,是否在等待锁。(搜索下栈里面是否有 block) 关于 app 出现的 anr,不能只看栈调用了系统方法,就转出给 frm,应该拿到手里,先做一些判断。 ● 判断 cpu 的使用情况,主要关注前三四个即可。 ● 判断当前负载如何 负载如果在 6-7 以下,属于正常,如果高于 8,在 11 以上,可以表明,当前系统负载过重,系统出现问题,需要再次定位。 负载过高,需要调查具体哪种原因,比如是 iowait 比重过高,系统频繁的读写操作引起。 负载一般,正常,那么就要去看下是否写的代码处会产生挂起等待,导致 anr ● 关注 log 信息,在发生 anr 的前一分钟内,看下系统在忙于哪些事情。 主要就是通过看 log 输出,查看下当前系统在干什么,核心可以围绕着 ams wms input 去看。

比如之前 systemui 出现的 time_tick 消息广播 anr ,由于我们的time_tick 是个频繁调用的广播,正常情况出现不了 anr 的,如果出现,我们需要怀疑的是系统的 cpu 当前到底在忙于做什么。比如常见的此处发生的时候,伴随着大量的 lowmem kill,那么问题可能会是系统瓶颈,或者 lowmem 配置不当,虚拟机内存配置不当等等,如果发现是此类问题,得出结论,优化系统性能。内存问题,可以去看下dumpsys meminfo查看下每个应用的内存占用情况。 04

如何判断是否此段代码在主线程: 方法一:使用 Looper 类判断 Looper.myLooper() != Looper.getMainLooper() 方法二:通过查看 Thread 类的当前线程 Thread.currentThread() == Looper.getMainLooper().getThread() 方法三:打印 Log,去看线程 id,看是否和进程号一样,一样是主线 程 线程的状态: ThreadState (defined at “dalvik/vm/thread.h “) THREAD_UNDEFINED = -1, / makes enum compatible with int32_t / THREAD_ZOMBIE = 0, / TERMINATED / THREAD_RUNNING = 1, / RUNNABLE or running now / THREAD_TIMED_WAIT = 2, / TIMED_WAITING in Object.wait() / THREAD_MONITOR = 3, / BLOCKED on a monitor / THREAD_WAIT = 4, / WAITING in Object.wait() / THREAD_INITIALIZING= 5, / allocated, not yet running / THREAD_STARTING = 6, / started, not yet on thread list / THREAD_NATIVE = 7, / off in a JNI native method / THREAD_VMWAIT = 8, / waiting on a VM resource / THREAD_SUSPENDED = 9, / suspended, usually by GC or debugger / log 查找 搜索 am_anr 会搜到一段异常信息。 iowait 故障: http://blog.csdn.net/lixin88/article/details/54345842 手动获取trace.txt: setenforce 0 (不执行这个,会无法写入文件) chmod 777 /data/anr rm /data/anr/traces.txt ps kill -3 PID adb pull data/anr/traces.txt  ~/traces.txt

DDMS:Start Methord Tracing: 使用traceview的方式,获取每个方法的耗时。

BlockCanary: 加入三方库,完成自动检测anr

05 trace.txt里面都有什么:




Jit thread pool worker thread 0 实时编译代码线程池**** JDWP 调试线程 HeapTaskDaemon 内存管理线程 ** 所有Binder开头的线程。这里为Binder:5859_1 关键**,这里Binder说明是一个跨进程的线程,于是乎我们调用AMS WMS等等一系列服务方法,都会在这个里面的堆栈体现出来,然后对应的system_server进程的trace.txt里面就有对应的响应的Binder在执行具体代码,有时我们的anr会在这里,就会是跨进程调用等待引起的anr。



实战: 1按键响应超时 从 LOG 可以看出 ANR 的类型,CPU 的使用情况,如果 CPU 使用量接近 100%,说明当前设备很忙,有可能是 CPU 饥饿导致了 ANR.如果 CPU 使用量很少,说明主线程被 BLOCK 了。如果 IOwait 很高,说明 ANR 有可能是主线程在进行 I/O 操作造成的。除了看 LOG,解决 ANR 还得需要 trace.txt 文件,adb shell bugreport 不仅可以获得 trace.txt,还可以获得当时的 memory 信息,以及其他进程信息。 2广播接收超时 我们去看下堆栈信息: 可以看到堆栈明显的指向,这段代码是个数据操作,以及数据库写入动作。 问题定位。 3ContentResolver  继续去看堆栈: 找到代码,去看,去修改: 4在 UI 线程进行网络数据的读写


主要看main:

如果它卡在一个方法的时候,等待,我们就要去找,是否有异步线程操作,主线程在等待结果的状态。之前看过一个问题是:主线程做了最大延时10s,来等待一个异步的结果,一般情况下,异步结果很快出来,但是异常情况,非常慢。最后定位的原因是异步操作,是基于数据库里面的图书列表,如果网络上推送下来很多书,然后查询数据,遍历以及整理数据,非常耗时,导致的anr。 5Memoryleak/Thread leak


继续分析


6Broadcast timeout 7普通的anr: 通常情况下我们只需要关注 total 的数值 ,total 数值高的情况下 关注一下 cpu 占用高的进程 字段及意义 : user : CPU 在用户态的运行时 kernel : CPU 在内核态运行的时间 idle : CPU 空闲时间,不包括 iowait 时间 iowait : CPU 等待 I/O 操作的时间 irq : CPU 硬中断的时间 softirq : CPU 软中断的时间 minor/major: 表示页错误次数 , 如果 ANR 发生时发现 CPU 使用率中 iowait 占比很高,可以通过查看进程的 major 次数来推断是哪个进程在进行磁 盘 I/O 操作 “+” ,说明该进程或线程是在最后两次 CPU 使用率采样时间段内新建的; 反之如果是“ -” ,说明该进程或线程在采样时间段内终止了

一个数目变大后,会严重耗时的地方:

这种一般需要注意,application的oncreate里面,不要写太多的init,不要太过庞大,需要异步的推迟去初始化或者第一次用时,再去初始化。之前遇到的问题为:google浏览器启动过程anr,最后你会发现原因在于google浏览器在启动的时候,加载了大量的class,导致启动的时候,时间耗费的太长,如果系统比较忙(android.bg cpu负载较大),很容易在启动时候anr。这种,只能从手机本身的性能去着手,比如出现anr的时候,kswapd cpu使用高,则可以认为,内核配置的交换大小不正确,如果logd.w等 cpu占用高,则说明log太频繁,需要去除一些log,如果是mm开头的一个(具体没记住),是管理sdcard的,所以和频繁操作sd卡有关。 06 总结: android设计了一种机制,保证主要的事务线不让无限的不返回,设计了anr。主要为UI线程,因为系统关注的就是它,其他线程不care,这是我们为什么看trace的时候,会直接跑去main线程先去看,同时注意,不是main线程也是需要看的,举个例子,之前遇到的问题为三方的一个亚马逊商场,在测试中发现了anr,随后进行查看trace,发现主线程栈是个正常执行流程中,发生。(就是这段代码本身不可能出现anr),然而发生了anr,我们从log中看到cpu中,亚马逊占比例非常高,然后我们找到亚马逊的trace文件,查看线程都有哪些,然后发现,当前后台出现了十几个线程,都在忙着下载图片,在write文件中,导致了cpu饥饿,发生anr。 07 予人玫瑰 ,手有余香。少些心机,多谢诚意。让心与心能靠在一起,温暖这世界的一隅角落。于是推荐一公号,欢迎大家围观。 总结自己在Android路上的一些学习经验,以及学习方法,支持投稿

08

参考文档: Android 信号处理面面观 之 trace 文件含义 : http://blog.csdn.net/rambo2188/article/details/7017241 Android ANR 分析 http://blog.csdn.net/yxz329130952/article/details/50087731