出现ANR的原因
三种常见类型
1:KeyDispatchTimeout(5 seconds) --主要类型
- 按键触摸事件派发超时ANR,一般阈值为5s(设置中开启ANR弹窗,默认有事件派发才会触发弹框ANR);
耗时的原因:下载;io异常;网络操作;数据库操作;高耗能的计算的等
如何避免:
a:UI线程尽量只做跟UI相关的工作。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。
b:耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理
c:尽量用Handler来处理UI thread和别的thread之间的交互。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等)
2:BroadcastTimeout(10 seconds)
- 广播阻塞ANR,一般阈值为10s(设置中开启ANR弹窗,默认不弹框,只有log提示);
如何避免:
a: 应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个Service。(此处需要注意的是可以在广播接受者中启动Service,但是却不可以在Service中启动broadcasereciver,关于原因后续会有介绍,此处不是本文重点)
b: 避免在IntentReceiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广播时需要向用户展示什么,你应该使用Notification Manager来实现。
3:ServiceTimeout(20 seconds) --小概率类型
- 服务超时ANR,一般阈值为20s(设置中开启ANR弹窗,默认不弹框,只有log提示);
1.Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc
2.AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc
3. Mainthread handler: handleMessage(), post*(runnable r), etc
为什么会超时呢?如何解决?
主要的解决办法自己最常用的就是不要在主线程中做耗时的操作,而应放在子线程中来实现。
a. 采用Handler+mesage的方式
b. 需要做一些和网络相互交互的耗时操作就采用asyntask异步任务的方式(它的底层其实Handler+mesage有所区别的是它是线程池)等,在主线程中更新UI。
如何定位问题
1. 先从main_log中找出哪个应用出现ANR,但是看不到具体的原因;
a. 关键字【Sending signal】 - 发生ANR的时间和生成trace.txt的时间
例:04-01 13:12:14.123 I/Process( 220): Sending signal. PID: 21404 SIG: 3
b. 关键字【ANR in process】或【Application is not responding】 - 直观的ANR
例:11-1621:41:42.560 I/ActivityManager( 1190):ANR in process:android.process.acore (last inandroid.process.acore)
例:13:12:11.572 I/InputDispatcher( 220):Application is notresponding:Window{2b263310com.android.email/com.android. email.activity.SplitScreenActivitypaused=false}. 5009.8ms since event, 5009.5ms since waitstarted
c. 关键字【CPU usage】 - 查看CPU的使用情况
如果CPU使用量接近100%,说明当前设备很忙,有可能是CPU饥饿导致了ANR;
例:内存不足导致block在创建bitmap上。 TOTAL:100% = 98% user + 1% kernel
如果CPU使用量很少,说明主线程被BLOCK了;
例:在UI线程进行网络数据的读写。 TOTAL:7%= 6% user + 1%kernel
如果IOwait很高,说明ANR有可能是主线程在进行I/O操作造成的。
例:IOWait很高,说明当前系统在忙于I/O,因此数据库操作被阻塞。 TOTAL:6.9% user + 8.2% kernel+84%io wait
d.关键字【KeyDispatchTimeout】或【BroadcastTimeout】或【ServiceTimeout】
导致ANR的类型。
2. 我总结了观察log文件的基本步骤 。
a. 如果是ANR问题 , 则搜索“ANR”关键词 。 快速定位到关键事件信息 。
b. 如果是ForceClosed 和其它异常退出信息,则搜索"Fatal" 关键词, 快速定位到关键事件信息 。
c. 定位到关键事件信息后 , 如果信息不够明确的,再去搜索应用程序包的虚拟机信息 ,查看具体的进程和线程跟踪的日志,来定位到代码 。
2. 再从data\anr\traces.txt文件中找到ANR的原因。系统会记录异常的位置、CPU和内存当时的使用情况。
例:Cmd line: system_server
具体的ANR
//显示进程id、ANR发生时间点、ANR发生进程包名
----- pid 19073 at 2015-10-08 17:24:38 -----
Cmd line: com.example.yanbo.myapplication
//一些GC等object信息,通常可以忽略
......
//ANR方法堆栈打印信息!重点!
DALVIK THREADS (18):
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 obj=0x7497dfb8 self=0x7f9d09a000
| sysTid=19073 nice=0 cgrp=default sched=0/0 handle=0x7fa106c0a8
| state=S schedstat=( 125271779 68162762 280 ) utm=11 stm=1 core=0 HZ=100
| stack=0x7fe90d3000-0x7fe90d5000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x0a2ae345> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x0a2ae345> (a java.lang.Object)
//真正导致ANR的问题点,可以发现是onClick中有sleep导致。我们平时可以类比分析即可,这里不详细说明。
at java.lang.Thread.sleep(Thread.java:985)
at com.example.yanbo.myapplication.MainActivity$1.onClick(MainActivity.java:21)
at android.view.View.performClick(View.java:4908)
at android.view.View$PerformClick.run(View.java:20389)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5743)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:988)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
......
//省略一些不常关注堆栈打印
......
参考:
通过Android trace文件分析死锁ANR