Android – 我如何调查一个ANR?

有没有find我的应用程序扔了一个ANR(应用程序没有响应)的方式。 我查看了/ data中的traces.txt文件,并查看了我的应用程序的跟踪信息。 这就是我所看到的痕迹。

DALVIK THREADS: "main" prio=5 tid=3 TIMED_WAIT | group="main" sCount=1 dsCount=0 s=0 obj=0x400143a8 | sysTid=691 nice=0 sched=0/0 handle=-1091117924 at java.lang.Object.wait(Native Method) - waiting on <0x1cd570> (a android.os.MessageQueue) at java.lang.Object.wait(Object.java:195) at android.os.MessageQueue.next(MessageQueue.java:144) at android.os.Looper.loop(Looper.java:110) at android.app.ActivityThread.main(ActivityThread.java:3742) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497) at dalvik.system.NativeStart.main(Native Method) "Binder Thread #3" prio=5 tid=15 NATIVE | group="main" sCount=1 dsCount=0 s=0 obj=0x434e7758 | sysTid=734 nice=0 sched=0/0 handle=1733632 at dalvik.system.NativeStart.run(Native Method) "Binder Thread #2" prio=5 tid=13 NATIVE | group="main" sCount=1 dsCount=0 s=0 obj=0x433af808 | sysTid=696 nice=0 sched=0/0 handle=1369840 at dalvik.system.NativeStart.run(Native Method) "Binder Thread #1" prio=5 tid=11 NATIVE | group="main" sCount=1 dsCount=0 s=0 obj=0x433aca10 | sysTid=695 nice=0 sched=0/0 handle=1367448 at dalvik.system.NativeStart.run(Native Method) "JDWP" daemon prio=5 tid=9 VMWAIT | group="system" sCount=1 dsCount=0 s=0 obj=0x433ac2a0 | sysTid=694 nice=0 sched=0/0 handle=1367136 at dalvik.system.NativeStart.run(Native Method) "Signal Catcher" daemon prio=5 tid=7 RUNNABLE | group="system" sCount=0 dsCount=0 s=0 obj=0x433ac1e8 | sysTid=693 nice=0 sched=0/0 handle=1366712 at dalvik.system.NativeStart.run(Native Method) "HeapWorker" daemon prio=5 tid=5 VMWAIT | group="system" sCount=1 dsCount=0 s=0 obj=0x4253ef88 | sysTid=692 nice=0 sched=0/0 handle=1366472 at dalvik.system.NativeStart.run(Native Method) ----- end 691 ----- 

我怎样才能找出问题所在? 跟踪中的方法都是SDK方法。

谢谢。

ANR在“主”线程中发生一些长操作时发生。 这是事件循环线程,如果它是繁忙的,Android不能在应用程序中处理任何进一步的GUI事件,从而抛出一个ANR对话框。

现在,在你发布的跟踪中,主线程似乎没有问题,没有任何问题。 它正在MessageQueue中闲置,等待另一个消息进来。在你的情况下,ANR可能是一个更长的操作,而不是永久阻塞线程的东西,所以事件线程在操作完成后恢复,并且你的踪迹经历了之后的ANR。

如果ANR是一个永久性块(死锁获得一些锁),检测ANR发生在哪里很容易,但是如果这只是一个暂时的延迟,则很难。 首先,检查你的代码,寻找可疑的点和长时间运行的操作。 示例可能包括在事件线程中使用套接字,锁,线程hibernate和其他阻塞操作。 你应该确保这些都发生在不同的线程中。 如果没有什么问题,请使用DDMS并启用线程视图。 这显示了您的应用程序中的所有线程与您所拥有的跟踪相似。 重现ANR,同时刷新主线程。 这应该告诉你在ANR的时候到底发生了什么

您可以在API级别9和更高版本中启用StrictMode 。

StrictMode最常用于在应用程序的主线程上捕获偶然的磁盘或networking访问,在这些线程中接收UI操作并进行animation。 通过保持应用程序的主线程响应,您还可以阻止向用户显示ANR对话框

 public void onCreate() { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectAll() .penaltyLog() .penaltyDeath() .build()); super.onCreate(); } 

使用penaltyLog()您可以观察adb logcat的输出,同时使用应用程序查看违规情况。

你想知道哪个任务持有UI线程。 跟踪文件给你一个提示find任务。 你需要调查每个线程的状态

线程的状态

  • 运行 – 执行应用程序代码
  • 睡觉 – 称为Thread.sleep()
  • 监视器 – 等待获取监视器locking
  • 等待在Object.wait()
  • 本机 – 执行本机代码
  • vmwait – 等待虚拟机资源
  • 僵尸 – 线程正在死亡的过程中
  • 初始化线程正在初始化(你不应该看到这一点)
  • 开始 – 线程即将开始(你不应该看到这一点)

专注于暂停,监视状态。 监视状态指示调查哪个线程,线程的SUSPENDED状态可能是死锁的主要原因。

基本调查步骤

  1. find“等待locking”
    • 你可以find监视器状态“Binder Thread#15”prio = 5 tid = 75 MONITOR
    • 你很幸运,如果发现“等待locking”
    • 例如:等待锁住由threadid = 74持有的<0xblahblah>(一个com.foo.A)
  2. 你可以注意到,“tid = 74”现在保存了一个任务。 所以去tid = 74
  3. tid = 74也许是SUSPENDED状态! find主要原因!

跟踪并不总是包含“等待locking”。 在这种情况下很难find主要原因。

过去几个月来我一直在学习android,所以我远离专家,但是我对ANR的文档感到非常失望。

大部分的build议似乎都是为了避开它们,或者盲目地查看你的代码来修复它们,这很好,但是在分析跟踪时我找不到任何东西。

有三件事情你真的需要用ANR日志来寻找。

1)死锁:当一个线程处于WAIT状态时,你可以查看细节来找出它是“被holdby =”的。 大多数情况下,它会自行保存,但是如果它被另一个线程占用,这可能是一个危险的信号。 去看看那个线程,看看它是什么。 你可能会发现一个循环,这是一个明显的迹象表明出了什么问题。 这是非常罕见的,但这是第一点,因为当它发生时,这是一场噩梦

2)主线程等待:如果你的主线程处于WAIT状态,检查它是否被另一个线程持有。 这不应该发生,因为你的UI线程不应该由后台线程。

这两种情况都意味着你需要重新修改你的代码。

3)主线上的繁重操作:这是ANR最常见的原因,但有时难以find并修复。 看看主线程的细节。 向下滚动堆栈跟踪,直到您看到您认出的类(从您的应用程序)。 看看跟踪中的方法,找出是否在这些地方进行networking调用,数据库调用等。

最后,我很抱歉无耻地插入自己的代码,你可以使用我在https://github.com/HarshEvilGeek/Android-Log-Analyzer上编写的python日志分析器。这将通过你的日志文件,打开ANR文件,查找死锁,find等待的主线程,在代理日志中find未捕获的exception,并以相对容易阅读的方式将其全部打印在屏幕上。; 阅读ReadMe文件(我即将添加)以了解如何使用它。 上星期帮了我一吨

无论何时你分析计时问题,debugging通常都无济于事,因为在断点处冻结应用程序将使问题消失。

最好的办法是在应用程序的不同线程和callback中插入大量的日志logging调用(Log.XXX()),并查看延迟的位置。 如果你需要一个堆栈跟踪,创build一个新的exception(只是实例化)并logging下来。

什么触发ANR?

通常,如果应用程序无法响应用户input,系统将显示ANR。

在任何情况下,你的应用程序执行一个潜在的冗长的操作,你不应该在UI线程上执行工作,而是创build一个工作线程,并在那里做大部分的工作。 这使UI线程(驱动用户界面事件循环)运行并防止系统推断您的代码已经冻结。

如何避免ANRs

Android应用程序通常默认在“UI线程”或“主线程”)上完全在单个线程上运行。 这意味着您的应用程序在UI线程中需要很长时间才能完成的任何事情都可以触发ANR对话框,因为您的应用程序没有给自己处理input事件或意图广播的机会。

因此,在UI线程中运行的任何方法都应该在该线程上尽可能less地工作。 具体来说,活动应尽可能less地在onCreate()和onResume()等关键生命周期方法中设置。 可能长时间运行的操作(如networking或数据库操作)或计算量大的计算(如调整位图大小)应在工作线程(或在数据库操作的情况下,通过asynchronous请求)完成。

代码:AsyncTask类的工作线程

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { // Do the long-running work in here protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } // This is called each time you call publishProgress() protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } // This is called when doInBackground() is finished protected void onPostExecute(Long result) { showNotification("Downloaded " + result + " bytes"); } } 

代码:执行工作线程

要执行这个工作线程,只需创build一个实例并调用execute():

 new DownloadFilesTask().execute(url1, url2, url3); 

资源

http://developer.android.com/training/articles/perf-anr.html

我的ANR问题,经过很多工作,我发现一个线程正在调用布局中不存在的资源,而不是返回一个exception,我得到了ANR …

您需要在/data/anr/traces.txt文件中查找“等待locking”

在这里输入图像描述

了解更多详情: 使用Android和Play工具实现高性能工程师(Google I / O '17)

基本上@Horyun Lee回答,我写了一个小python 脚本来帮助从traces.txt调查ANR。

如果您在系统上安装了grapvhviz ,则ANR将以graphviz输出为graphics。

 $ ./anr.py --format png ./traces.txt 

如果在文件traces.txt检测到ANR,则png将如下输出。 这更直观。

在这里输入图像描述

上面使用的示例traces.txt文件是从这里得到的。

考虑使用ANR-Watchdog库以高度详细的方式准确地跟踪和捕获ANR堆栈轨迹。 然后,您可以将它们发送到您的崩溃报告库。 我build议在这种情况下使用setReportMainThreadOnly() 。 请注意,发送到您的Google Play开发者控制台的ANR报告通常不够准确,无法查明确切的问题。

你可以让应用程序抛出冻结点的检查,或者在ANR发生时使应用程序强制退出。