[Android]ANR

出现ANR的原因

在Android里,应用程序的响应性是由ActivityManager和WindowManager系统服务监视的。当它监测到以下情况时,Android就会针对特定的应用程序显示ANR: 比如CPU使用过高、事件没有得到及时的响应、死锁等。

三种常见类型

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等)

2BroadcastTimeout(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提示);
UI线程包括如下:
     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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值