Android ANR 原因分析与检测

Android ANR 原因分析与检测

ANR,即应用程序不响应(Application Not Responding)。在主线程中进行某些耗时操作,超过一定时间,系统就会弹出ANR对话框。此时可以选择等待,让程序跑完,也可以选择关闭应用程序。

一、 ANR 原因分析

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

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

  2. 当前事件正在处理,但是由于耗时太长未能及时完成

本质上,产生ANR的原因有三种:KeyDispatchTimeout、BroadcastTimeout、ServiceTimeout

1.1 KeyDispatchTimeout

View的按键事件或者触摸事件这特定事件(5秒)内无法得到响应

1.2 BroadcastTimeout

BroadcastReceiver 的 onReceive()方法在特定时间(10秒)内无法完成处理(onReceive是运行在主线程的)

1.3 ServiceTimeout

Service 的各个生命周期函数在特定时间(20秒)内无法完成处理

二、ANR 避免

一个原则:主线程尽量只做UI相关的事,耗时操作(如文件读取,数据库读写等),计算量大的任务,在线程中执行,完成之后通过Handler来更新UI。

2.1 主线程

运行在主线程的有:
1. Activity的各个生命周期 onCreate(),onResume(),onDestory,点击/触摸事件等
2. Service的各个生命周期
3. BroadcastReceiver的onReceive()方法。
4. AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel
5. Mainthread handler: handleMessage(), post*(runnable r)
6. View的post()方法
在以上的这些方法内,尽量不要执行耗时的操作,如读写数据库,文件等。可以放在线程中运行,完成后再通知更新主线程。

2.2 异步处理技术

Android SDK提供了很多异步处理技术
1. Thread + Handler
2. HandlerThread
3. AsyncTask
4. IntentService
5. AsyncQueryHandler(用于ContentProvider上执行异步的CRUD)
6. Loader(Android 3.0引入的一个异步数据加载框架)

除了Android SDK提供的异步技术外,比较火的RxAnroid使用起来更加方便简洁,流程更加清晰,代码更加易于理解,功能更加强大(反正说是牛逼轰轰,确实也是很好用)。

三、ANR 定位分析

发生ANR后,可以结合Logcat日志和位于收集内部存储的/data/anr/trace.txt文件进行分析和定位。Cash和ANR的异常信息统计,一般可以接入第三的统计SDK,如友盟或者Bugly,不需要自己实现后台,便可以实时获得APP的异常统计信息,可以专注于问题的分析和定位。但是,为了更好地理解解决的思路,还是有必要了解信息是的来源和如何收集。

3.1 Logcat 日志信息

发生ANR后,可以查看Logcat日志,日志中有ANR的类型,时间等信息,参考浅谈ANR及log分析ANR

3.2 traces.txt 日志信息

有时候根据Logcat日志无法定位到问题的具体位置,这时候就需要traces.txt中的日志信息。
traces.txt位于设备的 /data/anr/traces.txt文件中(因为权限限制,在应用中无法读取,如果可以读取就可以上报给后台,实现自己的ANR日志收集SDK),链接上PC后可以通过adb命令导出:

adb pull /data/anr/traces.txt C:\Users\YourName\Desktop\traces.txt

如何分析traces.txt日志?参考:

  1. Android App优化之ANR详解
  2. android anr traces日志分析方法

3.3 第三方统计SDK

Logcat 日志和trace.txt日志都需要有设备才能获取到,而实际中几乎不可能拿到用户的设备来查看日志,所以,有必要有一个后台来收集错误信息。第一种方法是自己实现Crash和ANR信息收集;第二种方法是利用现有的第三方平台,如Bugly友盟。使用第三方平台的好处是节约成本,专注于ANR的定位和问题的解决即可。

四、ANR 检测

可以借助一些工具,在调试阶段进行违规代码的检测

4.1 StrictMode

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

  1. 线程策略ThreadPolicy
    1. detectCustomSlowCalls 检测自定义耗时操作
    2. detectDiskReads 检测是否存在磁盘读取操作
    3. detectDiskWrites 检测是否存在磁盘写入操作
    4. detectNetwork 检测是否存在网络操作
  2. 虚拟机策略VmPolicy
    1. detectActivityLeaks 检测是否存在Activity泄漏
    2. detectLeakedClosableObjects 检测是否存在未关闭的Closable对象泄漏
    3. detectLeakedSqlLiteObject 检测是否存在Sqlite对象泄漏
    4. setClassInstanceLimit 检测类实例个数是否超出限制

严格模式的使用并不复杂,在自定义的Application类或者MainActivity的onCreate()方法中加入以下代码即可:

    @Override
    public void onCreate() {
        super.onCreate();
        if (BuildConfig.DEBUG) {      // 只在debug使用,release不使用严格模式
           StrictMode.enableDefaults();
        }
    }

enableDefaults()的实现如下:

    /**
     * Enable the recommended StrictMode defaults, with violations just being logged.
     *
     * <p>This catches disk and network access on the main thread, as
     * well as leaked SQLite cursors and unclosed resources.  This is
     * simply a wrapper around {@link #setVmPolicy} and {@link
     * #setThreadPolicy}.
     */
    public static void enableDefaults() {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                                   .detectAll()
                                   .penaltyLog()
                                   .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                               .detectAll()
                               .penaltyLog()
                               .build());
    }

启用了严格模式,在Logcat中过滤:tag = StrictMode即可看到输出的日志信息。然后根据输出的日志信息,检测是否存在问题,然后及时改正。

4.2 BlockCanary

BlockCanary 是一个非侵入式的性能监控函数库,它的用法和LeakCanary类似,只不过后者监控应用的内存泄漏,而BlockCanary主要用来监控应用的主线程卡顿,它的基本原理是利用主线程的消息队列处理机制,通过对比消息分发开始和结束的时间点来判断是否超过设定的时间,如果是则判断线程卡顿,它的集成很简单,在build.gradle中添加依赖: compile ‘com.github.moduth:blockcanary-android:1.2.1’,然后在Application中进行配置和初始化即可。

@Override
    public void onCreate() {
        super.onCreate();
        BlockCanary.install(this, new AppBlockCanaryContext()).start();
    }

更多参考:blockcanaryBlockCanary解析

五、扩展

一、实现ANR信息收集SDK

前面介绍中的 Logcat 日志和 traces.txt 文件日志可以作为分析ANR的主要信息,而Logcat日志需要连接到PC利用IDE来查看,traces.txt文件/data/anr/traces.txt是没有读取权限的。所以,如何收集这些信息,上报到后台?(PS: bugly等第三方平台是如何实现的呢?)

六、参考

  1. Android App优化之ANR详解
  2. Android高级进阶
  3. 浅谈ANR及log分析ANR
  4. blockcanary
  5. BlockCanary解析
原创文章 25 获赞 12 访问量 1万+

猜你喜欢

转载自blog.csdn.net/half_bottle/article/details/78108424