ANR?不存在的

什么是ANR?

ANR是Application Not Responding的缩写,即应用程序未响应。在android中,当你的应用程序在一段时间内响应不够灵敏,即其界面线程处于阻塞状态的时间过长,就会触发ANR错误。

这个时候如果你的应用位于前台,android系统会向使用者显示一个对话框,用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。这种形式类似于windows系统中的“该程序未响应”。

ANR

 

如何去规避?

想要去规避ANR在你开发的android应用中的出现,那么首先你得知道哪些情况会导致ANR的出现。可能我们见到的最多的答案就是:

  • 输入事件(按键和触摸事件)在5s内没被处理;
  • BroadcastReceiver的onRecieve方法在10s内没处理完;
  • service的各个生命周期在20s内没有执行完;

以上的答案其实没什么问题,只是不够严谨,这里的5s、10s、20s其实会受到手机自身性能的影响,实际测试的情况,不同的手机可能会有所偏差,而在广播中只有前台广播需要在10s内处理完成,后台广播响应的最大时长为60s。严格意义上来讲还有一些其他的情况也可能会导致ANR,比如ContentProvider的publish在10s内没进行完等。

总体上而言,当主线程做一些耗时的I/O操作、计算或者主线程处于阻塞状态,又或者主线程处于死锁状态时都会导致我们的应用程序未响应。找到了产生ANR的条件,那么如何去规避它就很简单了,就是反着来嘛。这里我们总结一下。

  1. 尽量避免应用在主线程中执行耗时的的I/O操作。
  2. 避免应用在主线程中进行长时间的计算工作。
  3. 主线程与另一个线程争夺资源时,因尽量保证主线程优先执行,避免主线程处于阻塞状态。
  4. 通过代码避免主线程在进程中或通过 binder 调用与另一个线程之间发生的死锁。

 

怎样去检测?

事无绝对,人无完人。我们不可能保证写的每一行代码中都不会导致ANR的出现,那么一旦出现了ANR我们又该如何去找出原因呢。

Android Vitals​​​​​​​(能有效的收集ANR的发生率)

这里首先要介绍一下Android Vitals,Android Vitals是Google推出的一项计划,旨在改善Android设备的稳定性和性能,当用户选择启用该计划时,其android设备会记录各项指标,其中就有一项指标叫做ANR的发生率。开发者们可以很好的通过Google Play查看自身应用的Android Vitals各项指标,从而找到应用ANR的发生情况。当然,这些只是接入Google Play的福音,一般我们做国内的项目是没法享受到的。

TraceView(拉去追踪信息文件,分析错误报告)

TraceView是DDMS中自带的一款图形化显示和分析跟踪日志的文件的工具。它也是我们检测ANR的重要工具。首先我们可以通过按钮或者代码调用的方式捕捉你认为可疑的代码块。

Debug.startMethodTracing("anr_trace");    //开始捕捉代码块
  ...
Debug.stopMethodTracing();    //结束

通过以上方式可以生成一个anr_trace.trace文件并将它保存到设备上(trace,txt文件存储路径由dalvik.vm.stack-trace-file系统属性决定),这时我们可以使用adb命令(adb  pull  /sdcard /anr_trace.trace  /tmp)将trace文件导出到电脑,然后放到DDMS中打开并分析它。通过这种方式我们可以很轻松的分析出那些在主程序中执行缓慢的代码、死锁、执行缓慢的广播等能引发ANR的代码,从而优化它。

这里我们可以通过一个例子来直观地分析,下面是一张直观地分析图。

分析图

可以看到截取的代码块是一段onClick方法,那么这是一段主线程中的代码,在这段代码中存在一个sort排序。Incl Cpu Time表示当前方法执行时间,sort方法明显用了8065.841毫秒即8s多。Incl Cpu Time%表示该方法在整个代码块耗时中所用耗时的百分比,这里sort方法耗时也高达99.9%,可见是sort方法导致了主线程执行速度缓慢,从而可能会导致ANR。找到了原因,那么解决办法就呼之欲出了,正确的做法就是将sort排序放在工作线程中执行。

获取ANR错误报告文件(当发生ANR时,系统会自动保存跟踪信息)

这是一种非常直观地分析ANR的方式,Android系统会在遇到ANR时存储跟踪信息。在较低的android版本中,应用发生ANR时,系统会将跟踪信息存储到/data/anr/traces.txt文件中。而在较新的版本中,则会有多个/data/anr/anr_*文件可供存储。我们还是可以使用adb命令来从设备上拉取错误报告文件,然后打开并分析它。

StrictMode(严格模式)

StrictMode是一个开发者工具,常常被用来捕获在应用主线程中发生的磁盘I/O、网络访问违例等行为。

下面还是通过一个例子直观地来体会,首先我们需要在应用程序中接入StrictMode。我们可以在application或者activity的onCreate方法中加入以下代码。

        //严格模式中的ThreadPolicy线程策略
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()   
                .penaltyLog()
                .build());

        //严格模式中的VmPolicy虚拟机策略
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects()   //泄漏的Sqlite对象
                .detectLeakedClosableObjects()  //未关闭的Closable对象泄漏
                .penaltyLog()
                .penaltyDeath()
                .build());

然后编写一段违规的代码。

        final TextView tvHW = findViewById(R.id.tv_hw);
        tvHW.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Debug.startMethodTracing("anr_trace");
                try {
                    Thread.sleep(10*1000);
                    tvHW.setText("hello ledding");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Debug.stopMethodTracing();
            }
        });

最后,运行下代码,通过logcat查看StrictMode捕捉的信息。

logcat

通过日志可以看到是strictmode的DiskReadViolation(磁盘读取操作)异常,总共用时10257ms,发生在onClick中。当然StrictMode的功能远不止这些,它还能用来检测代码中的内存泄漏等,这里也就不再延伸了。

 

总结

ANR是android一种常见的问题,也是android开发者不可避免的知识区,一个友好的app应该规避ANR现象的出现,从而大大的提高用户的体验度。掌握并合理的运用这些工具是解决ANR的利器。工欲善其事,必先利其器。

发布了17 篇原创文章 · 获赞 6 · 访问量 4344

猜你喜欢

转载自blog.csdn.net/ledding/article/details/105288195