Android ANR产生的原因以及其定位分析

前言

  

   ANR是Android中一个独有的概念,它的全称是Application Not Responding(应用程序无响应)。

   相信从事Android开发的同学,或多或少都遇到过,对于高质量的代码,ANR在开发者自测过程中可能不会经常遇到,但一旦测试人员进行Monkey测试,ANR出现的概率就比较高了,如何快速分析定位并解决,是开发者的必修课。

  ANR的直观体验是用户在操作APP的过程中,感觉界面卡顿,比如按下某个按钮,打开某个页面等,当卡顿超过一定时间(一般是5秒)时就会出现ANR对话框,如下图所示:


 这时查看Logcat,一般可以发现ANR以及traces.txt等字样。可以发现,出现ANR主要是因为我们在主线程中做了耗时操作。这时你可以选择“等待”按钮,等待应用程序结束主线程耗时操作,或者选择“确定”按钮,结束这个应用程序。


1、ANR产生的原因


  只有当应用程序的UI线程响应超时才会引起ANR,超时产生原因一般有两种:

   · 当前的事件没有机会得到处理,列如UI线程正在响应另外一个事件,当前事件由于某种原因被阻塞了。

   · 当前的事件正在处理,但是由于耗时太长没能及时完成。

  根据ANR产生的原因不同,超时时间也不尽相同,从本质上讲,产生ANR的原因有三种,大致可以对应到Android中四大组件中的三个(Activity/View、BroadcastReceiver和Service)。

KeyDispatchTiemout
 
 
  • 1
  • 2
  • 1

  最常见的一种类型,原因是View的按键事件或者触摸事件在特定的时间(5秒)内无法得到响应。

BroadcastTiemout
 
 
  • 1
  • 2
  • 1

 原因是BroadcastReceiver的onReceive()函数运行在主线程中,在特定的时间(10秒)内无法完成处理。

ServiceTiemout
 
 
  • 1
  • 2
  • 1

 比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20秒)内无法完成处理。


2、典型的ANR问题场景


 · 应用程序UI线程存在耗时操作,例如在UI线程中进行网络请求、数据库操作或者文件操作,可能会导致UI线程无法及时处理用户输入等。当然在Android4.0之后,如果在UI线程中进行网络操作,将会抛出NetworkOnMainThreadException异常。

 · 应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入。

 · 耗时的动画需要大量的计算工作,可能导致CPU负载过量。


3、ANR的定位和分析

  当发生ANR时,开发者可以通过结合Logcat日志和生成的位于手机内部存储的/data/anr/traces.txt文件进行定位和分析。


4、ANR的避免和检测

  为了避免在开发中引入可能导致应用发生ANR的问题,除了切记不要在主线程中作耗时操作,我们也可以借助于一些工具来进行检测,从而更有效的避免ANR的引入。

  4.1 StrictMode

  严格模式StrictMode是Android SDK提供的一个用来检测代码中是否存在违规操作的工具类,StrictMode主要检测两大类问题:

   · 线程策略ThreadPolicy

     - detectCustomSlowCalls: 检测自定义耗时操作。

     - detectDiskReads: 检测是否存在磁盘读取操作。

     - detectDiskWrites: 检测是否存在磁盘写入操作。

     - detectNetwork: 检测是否存在网络操作。


    · 虚拟机策略VmPolicy

     - detectActivityLeaks: 检测是否存在Activity泄漏。

     - detectLeakedClosableObjects: 检测是否存在未关闭的Closeable对象泄漏。

     - detectLeakedSqlLiteObjects: 检测是否存在Sqlite对象泄漏。

     - setClassInstanceLimit: 检测类实例个数是否超过限制。


    4.2 BlockCanary

   BlockCanary是一个非侵入式的性能监控函数库,它的用法和LeakCanary类似,只不过LeakCanary监控应用的内存泄漏,而BlockCanary主要用来监控应用主线程的卡顿。它的基本原理是利用主线程的消息队列处理机制,通过对比消息分发开始和结束的时间点来判断是否超过设定的时间,如果是,即判断为主线程卡顿。它的集成很简单,首先在build.gradle中添加在线依赖,如下:

dependencies {
      compile 'com.github.moduth:blockcanary-android:1.2.1'
      // 仅在debug包中启用BlockCanary进行卡顿监控和提示的话,可以这么用
      debugCompile 'comgithub.moduth:blockcanary-android:1.2.1'
      releaseCompile 'comgithub.moduth:blockcanary-no-op:1.2.1'
}

  然后在Application类中进行配置和初始化即可,

public class DemoApplication extends Application {
       @Override
       public void onCreate() {
             // 在主进程初始化调用
             BlockCanary.install(this, new AppBlockCanaryContext()).start());
       }
}


  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值