Android 如何监控和解决ANR问题

简介: Android 如何监控和解决ANR问题

一、ANR的定义


Android全称是Application Not Response,即程序无响应。ANR的直观体验是用户在操作APP的过程中,感觉界面卡顿,如果

Android应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误,如下图所示,ANR 对话框会为用户提供强行退出应用的选项。


1fb78bbf1ae0e5a8b4bdc6dc896f4d0a_format,png.png


当点击了Close

app或者由于ANR引起了闪退之后,这时查看Logcat,一般可以发现ANR以及trace.txt等字样,可以知道出现ANR主要原因是我们在主线程做了太多耗时操作,这时你可以选择等待按钮,等待应用程序结束主线程的耗时操作,或者选择确定按钮,结束这个应用程序,ANR对于一个应用来说是不能承受之痛,其影响并不比应用发生Crash小。


二、ANR类型


出现ANR的一般有以下几种类型:


  • KeyDispatchTimeOut :最常见的一种类型,原因是View的按键事件或者触摸事件在特定的时间(5秒)内无法得到响应。
  • BroadcaseTimeOut:原因是BroadcastReceiver的onReceiver()函数运行在主线程中,在特定的时间(10秒)内无法完成处理。
  • ServiceTimeOut:比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20秒)内无法完成处理。
  • ContentProviderTimeout:ContentProvider在10S内无法完成处理。


三、ANR原因


从应用的角度上来讲,它的原因可以归结为以下几种:


  • 应用在主线程上非常缓慢地执行涉及 I/O 的操作。
  • 应用在主线程上进行长时间的计算。
  • 主线程在对另一个进程进行同步 binder 调用,而后者需要很长时间才能返回。
  • 主线程处于阻塞状态,等待子线程的长时间耗时操作完成。
  • 主线程在进程中或通过 binder 调用与另一个线程之间发生死锁。主线程不只是在等待长操作执行完毕,而且处于死锁状态。


四、如何检测和诊断ANR问题


1、开发中检测ANR

  • StrictMode(严格模式)


开发中由于个人原因,多多少少都会可能写出造成ANR的代码,要想在开发中及时发现问题,可以使用StrictMode有助于您在开发应用时发现主线程上的意外

I/O 操作。


严苛模式是一个避免你不小心把网络或者IO操作放在UI线程操作的开发工具,从而实现避免ANR。使用严苛模式,系统检测出主线程违例的情况会做出相应的反应,如日志打印,屏幕闪烁(需要在开发者选项里面打开启用严格模式)等。


  • 启用后台 ANR 对话框


只有在设备的开发者选项中启用了显示所有 ANR 时,Android 才会针对花费过长时间处理广播消息的应用显示 ANR

对话框。我们可以通过打开这个选项,在开发中及早发现相关问题。


  • TraceView


TraceView 是 Android SDK 中内置的一个工具,它可以加载 trace

文件,用图形的形式展示代码的执行时间、次数及调用栈,便于我们分析。(注意:Android Studio3.2之后已经弃用)


  • CPU Profiler


Android Studio3.2之后,CPU

Profiler替代了TraceView,我们可以通过在通过记录应用交互过程获取相关方法执行顺序和耗时图,从而分析哪些方法耗时过长导致ANR。


2、线上ANR数据收集


在我们日常开发中经常遇到的ANR问题都是线上用户使用时发现的ANR问题,如果是我们开发中就已经发现,那是非常好解决的,只需要聚焦于新增代码块即可,但是如果是针对线上版本,那么我们就需要对线上数据进行监控,很多公司都会自主研发APM系统,或者是借用第三方的,抑或使用Google官方的,都是可以统计到ANR的数据。其实有时候发现站在巨人的肩膀上去做一些事情,也许效率会更高,以下是一些常用的ANR数据收集工具:


  • 国外
  • Google vitals:Google Play自带的性能统计工具。
  • Firebase Crashlytics:Google Cloud Platform为应用开发者们提供的实时性能分析系统。
  • ACRA:在Goolge Play上有2.68%实用率的ACRA库。
  • 国内
  • Bugly:腾讯Bugly项目组推出移动应用崩溃监控分析平台,提供崩溃、脚本错误、ANR(Android)/卡顿(iOS)问题等监控分析服务。
  • xCrash:爱奇艺开源的一个性能监控的SDK。
  • Umeng:国内移动统计分析服务平台,提供统计分析、更新、分享、推送等服务,其中,错误分析也是在统计分析的基础上添加。
  • Testin:国内云测平台,服务升级后,提供云测,APM服务(包括性能监控,错误上报等),众测等服务。


五、如何解决问题


无论我们通过线上还是开发中发现了ANR问题,我们都需要思考怎么去解决。


1、修改主线程上耗时的代码


通过TraceView或者CPU Profiler可以找到应用中主线程忙碌时间超过5s的位置,然后把此处代码移到子线程操作。如一些网络操作、耗时计算等。


2、锁的竞争导致堵塞


如果主线程参与锁的竞争,有可能会导致主线程持续等待锁而造成堵塞的问题,从而引发ANR。所以最好还是避免主线程出现竞争锁的情况。


3、死锁


如果某资源被另一个线程所持有,而该线程又在等待主线程的资源,就会陷入死锁情况导致ANR。


4、广播接收器执行速度慢


如在广播接收器的onReceive()执行了长时间的耗时操作,就会可能导致ANR,所以应该把耗时操作放到子线程操作。


六、如何实现ANR监控


站在巨人的肩膀固然好,但是如果能够自己变成巨人那就更好了,所以作为一个有追求的开发者,我们当然要思考如何监控APP的ANR问题。


目前流行的ANR检测方案有开源的BlockCanary 、ANR-WatchDog、SafeLooper,xCrash,

还有根据谷歌原生系统接口监测的方案:FileObserver。


1、BlockCanary


BlockCanary是Android平台上的一个轻量的,非侵入式的性能监控组件,可以在使用应用的时候检测主线程上的各种卡顿问题,并可通过组件提供的各种信息分析出原因并进行修复。


原理:


  • (1) 在Looper里面的loop方法里面有一个Printer打印日志,它在每个Message处理的前后被调用,而如果主线程卡住了,就是dispatchMessage里卡住了。


public static void loop() {
      // ....
      for (;;) {
          // ...
          // This must be in a local variable, in case a UI event sets the logger
          final Printer logging = me.mLogging;
          if (logging != null) {
              logging.println(">>>>> Dispatching to " + msg.target + " " +
                              msg.callback + ": " + msg.what);
          }
          // ...
          msg.target.dispatchMessage(msg);
          // ...
          if (logging != null) {
              logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
          }
         // ...
      }
  }


  • (2) 每个线程只有一个Looper,而只要重新设置主线程Looper里面的Printer即可重写println方法,然后在这里面进行前后两次消息处理的时间差,从而计算是否有发生ANR。


// 创建自定义Printer
      ...
      @Override
      public void println(String x) {
          if (!mStartedPrinting) {
              mStartTimeMillis = System.currentTimeMillis();
              mStartThreadTimeMillis = SystemClock.currentThreadTimeMillis();
              mStartedPrinting = true;
          } else {
              final long endTime = System.currentTimeMillis();
              mStartedPrinting = false;
              if (isBlock(endTime)) {
                  notifyBlockEvent(endTime);
              }
          }
      }
      private boolean isBlock(long endTime) {
          return endTime - mStartTimeMillis > mBlockThresholdMillis;
      }
      ...


  • (3)设置自定义Printer到主线程Looper


Looper.getMainLooper().setMessageLogging(mainLooperPrinter);


  • 优点:灵活配置可监控常见APP应用性能也可作为一部分场景的ANR监测,并且可以准确定位ANR和耗时调用栈。


  • 缺点:
  • (1) 谷歌已经明确标注This must be in a local variable, in case a UI event sets the logger这个looger对象是可以被更改的,已经有开发者遇到在使用WebView时logger被set为Null导致BlockCanary失效,只能让BlockCanary在WebView初始化之后调用start。
  • (2) 如果dispatchMessage执行的非常久是无法触发BlockCanary的逻辑。
  • (3) 在Printer输出之前,有一段代码queue.next()也会可能发生ANR,从而造成无法监控到ANR。
  • (4) 无法监控CPU资源紧张造成系统卡顿无法响应的ANR。


2、ANR-WatchDog


ANR-WatchDog是参考Android

WatchDog机制,起了个单独线程向主线程发送一个变量+1的操作,然后休眠一定时间阈值(阈值可自定义,例如5s),休眠过后再判断变量是否已经+1,如果未完成则ANR告警。在GP上有有2.68%实用率的ACRA库也推荐使用这种方式。


  • 原理:如下图所示。


f8cb2200da82756368a2fc5892c79812_format,png.png


  • 优点:
  • (1) 兼容性好,无需适配机型。
  • (2) 无需改动APP逻辑代码,非侵入性。
  • (3) 性能影响不大。
  • 缺点:
  • (1) 无法保证能捕获所有ANR,对阈值设置影响捕获概率。如时间过长,中间发生的ANR则可能被遗漏掉。


3、SafeLooper


SafeLooper是一个第三方开源的库,主要用于捕获未知异常,避免出现ANR弹窗,从而在捕获到异常时获取ANR信息。


  • 原理:SafeLooper其实就是一个Runnable,启动后把这个SafeLooper(消息)post到主线程,并且会轮训主线程的消息队列,通过反射把新消息进行处理,然后判断新消息的处理过程是否会出现ANR导致的异常,如果是就将其捕获,并把异常信息通过uncaughtException回调给用户进行处理。主要流程如下图所示。


3f35970f0b08d46589737b46c0f2e8f7_format,png.png


  • 优点:使用AOP思想进行异常捕获的思想值得借鉴。
  • 缺点:使用反射会影响性能。


4、FileObserver


通过复现ANR场景,可以发现/data/anr文件夹会伴随ANR发生而变化,所以可以通过监听/data/anr目录下是否有文件写入,如果有的话就认为发生了ANR,像Bugly对ANR的监控就是用这种方法来实现的。


  • 原理:自定义FileObserver监听/data/anr目录下文件是否有新增".tarce"结尾的文件,如果有则认为发生ANR,并导出trace文件,注意如果当多个APP同时发生ANR,里面会有多个trace文件,需要对包名时间等进行过滤。
  • 优点:
  • (1) 基于原生接口调用,时机和内容准确。
  • (2) 无性能问题,实现简单。
  • 缺点:
  • (1) /data/anr目录可能不会在发生ANR时马上写入文件,可能会发生滞后,从而导致收集到的trace文件和线程信息不准确。


5、xCrash


xCrash是爱奇艺开源的一个性能监控SDK,它的实现方案和以上四种不太一样,它是通过监控系统的信号量的变化,从而确定是否发生了ANR,然后再整理输出Tombstone文件。由于我对系统信号量了解不多,这里就贴出官方文档的原理图,欢迎了解的大神补充。


9272434221b1832a624cf13f992e91fb_format,png.png


七、相关参考文章



相关文章
|
1月前
|
移动开发 监控 Android开发
Android & iOS 使用 ARMS 用户体验监控(RUM)的最佳实践
本文主要介绍了 ARMS 用户体验监控的基本功能特性,并介绍了在几种常见场景下的最佳实践。
|
数据库 Android开发
android 如何调查并解决 ANR
android 如何调查并解决 ANR
72 0
|
存储 JSON 监控
APM监控 · 入门篇 · Android端测监控平台建设(1)
APM 全称 Application Performance Management & Monitoring (应用性能管理/监控) 性能问题是导致 App 用户流失的罪魁祸首之一,如果用户在使用我们 App 的时候遇到诸如页面卡顿、响应速度慢、发热严重、流量电量消耗大等问题的时候,很可能就会卸载掉我们的 App。这也是我们在目前工作中面临的巨大挑战之一,尤其是低端机型。
2911 0
APM监控 · 入门篇 · Android端测监控平台建设(1)
|
13天前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
38 8
|
2月前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
19 1
|
存储 监控 Android开发
Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
|
4月前
|
算法 Java API
Android性能优化面试题经典之ANR的分析和优化
Android ANR发生于应用无法在限定时间内响应用户输入或完成操作。主要条件包括:输入超时(5秒)、广播超时(前台10秒/后台60秒)、服务超时及ContentProvider超时。常见原因有网络、数据库、文件操作、计算任务、UI渲染、锁等待、ContentProvider和BroadcastReceiver的不当使用。分析ANR可借助logcat和traces.txt。主线程执行生命周期回调、Service、BroadcastReceiver等,避免主线程耗时操作
62 3
|
5月前
|
XML 监控 安全
Android App性能优化之卡顿监控和卡顿优化
本文探讨了Android应用的卡顿优化,重点在于布局优化。建议包括将耗时操作移到后台、使用ViewPager2实现懒加载、减少布局嵌套并利用merge标签、使用ViewStub减少资源消耗,以及通过Layout Inspector和GPU过度绘制检测来优化。推荐使用AsyncLayoutInflater异步加载布局,但需注意线程安全和不支持特性。卡顿监控方面,提到了通过Looper、ChoreographerHelper、adb命令及第三方工具如systrace和BlockCanary。总结了Choreographer基于掉帧计算和BlockCanary基于Looper监控的原理。
88 3
|
6月前
|
存储 监控 Java
Android Service之设备存储空间监控 DeviceStorageMonitorService
Android Service之设备存储空间监控 DeviceStorageMonitorService
122 2
|
6月前
|
缓存 Java 数据库
Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
110 0