您的位置:首页 > 移动开发 > Android开发

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

2017-07-04 21:46 411 查看

前言

  

   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());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息