Android - 如何调查ANR?

时间:2009-04-01 06:28:26

标签: android performance android-anr-dialog

有没有办法找出我的应用程序扔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方法。

感谢。

11 个答案:

答案 0 :(得分:117)

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

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

如果ANR发生永久性阻塞(例如死锁获取某些锁定),则检测ANR发生的位置很容易,但如果它只是暂时的延迟则更难。首先,查看代码并查找可用的点和长时间运行的操作。示例可以包括在事件线程内使用套接字,锁,线程休眠和其他阻塞操作。你应该确保这些都发生在不同的线程中。如果没有任何问题,请使用DDMS并启用线程视图。这会显示应用程序中的所有线程与您拥有的跟踪类似。重现ANR,同时刷新主线程。这应该向你展示ANR时正在发生的事情

答案 1 :(得分:93)

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

  

StrictMode最常用于捕获意外磁盘或网络   访问应用程序的主线程,UI操作在哪里   收到和动画发生。保持应用程序的主线程   响应,您还阻止向用户显示ANR对话

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

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

答案 2 :(得分:67)

您想知道哪个任务持有UI线程。跟踪文件为您提供了查找任务的提示。你需要调查每个线程的状态

线程状态

  • 正在运行 - 执行应用程序代码
  • 睡觉 - 名为Thread.sleep()
  • 监视器 - 等待获取监视器锁
  • 等待 - 在Object.wait()
  • native - 执行本机代码
  • vmwait - 等待VM资源
  • 僵尸 - 线程正在死亡
  • init - 线程正在初始化(你不应该看到这个)
  • 开始 - 线程即将开始(你也不应该看到这个)

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

基本调查步骤

  1. 发现&#34;等待锁定&#34;
    • 你可以找到监控状态&#34; Binder Thread#15&#34; prio = 5 tid = 75 MONITOR
    • 如果发现&#34;等待锁定&#34;
    • ,你很幸运
    • 示例:等待锁定&lt; 0xblahblah&gt; (com.foo.A)由threadid = 74
    • 持有
  2. 您可以注意到&#34; tid = 74&#34;现在就完成任务。所以去tid = 74
  3. tid = 74也许是SUSPENDED状态!找到主要原因!
  4. 跟踪并不总是包含&#34;等待锁定&#34;。在这种情况下,很难找到主要原因。

答案 3 :(得分:11)

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

大多数建议似乎都是为了避免它们或通过盲目查看代码来修复它们,这很好,但我找不到任何关于跟踪的信息。

使用ANR日志确实需要查找三件事。

1)死锁:当线程处于WAIT状态时,您可以查看详细信息以查找它的“holdby =”。大多数情况下,它将由它自己保留,但如果它由另一个线程持有,那很可能是一个危险信号。去看看那个帖子,看看它的含义。你可能会发现一个循环,这是一个明显的迹象,表明出了问题。这是非常罕见的,但这是第一点,因为当它发生时,这是一场噩梦

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

这两种情况都意味着您需要对代码进行重大修改。

3)主线程上的大量操作:这是ANR的最常见原因,但有时候更难找到并修复。看一下主要的线程细节。向下滚动堆栈跟踪,直到看到您识别的类(来自您的应用程序)。查看跟踪中的方法,并确定您是否在这些地方进行网络呼叫,数据库呼叫等。

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

答案 4 :(得分:4)

每当您分析时序问题时,调试通常都无济于事,因为在断点处冻结应用程序会使问题消失。

最好的办法是在应用程序的不同线程和回调中插入大量日志记录调用(Log.XXX()),并查看延迟的位置。如果需要堆栈跟踪,请创建一个新的Exception(只是实例化一个)并记录它。

答案 5 :(得分:3)

什么触发ANR?

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

在您的应用程序执行可能冗长的操作的任何情况下,您不应该在UI线程上执行工作,而是创建工作线程并在那里完成大部分工作。这使得UI线程(驱动用户界面事件循环)保持运行,并阻止系统断定您的代码已冻结。

如何避免ANR

Android应用程序通常完全在单个线程上运行,默认情况下为“UI线程”或“主线程”。这意味着您的应用程序在UI线程中执行的任何操作都需要很长时间才能完成,因为您的应用程序没有给自己处理输入事件或意图广播的机会。

因此,在UI线程中运行的任何方法都应该在该线程上尽可能少地工作。特别是,活动应该尽可能少地设置在关键的生命周期方法中,例如onCreate()和onResume()。潜在的长时间运行操作(如网络或数据库操作)或计算成本高昂的计算(如调整位图大小)应在工作线程中完成(或者在数据库操作的情况下,通过异步请求)。

代码:具有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");
    }
}

代码:执行工作线程

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

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

来源

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

答案 6 :(得分:1)

我的ANR问题,经过大量工作我发现一个线程正在调用布局中不存在的资源,而不是返回异常,我得到了ANR ......

答案 7 :(得分:1)

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

enter image description here

了解更多详情:Engineer for High Performance with Tools from Android & Play (Google I/O '17)

答案 8 :(得分:0)

基于@Horyun Lee回答,我写了一个小python script来帮助调查来自traces.txt的ANR。

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

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

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

enter image description here

上面使用的示例traces.txt文件来自here

答案 9 :(得分:0)

考虑使用ANR-Watchdog库以高级别的细节准确跟踪和捕获ANR堆栈跟踪。然后,您可以将它们发送到崩溃报告库。我建议在此方案中使用setReportMainThreadOnly()。您可以让应用程序抛出冻结点的非致命异常,或者在ANR发生时强制退出应用程序。

请注意,发送到Google Play开发者控制台的标准ANR报告通常不够准确,无法确定问题所在。这就是需要第三方库的原因。

答案 10 :(得分:0)

不确定这是否有帮助。我的问题是应用程序崩溃并冻结我的设备,然后强制它在带有 android 10 的设备上重新启动,但在 android 6 上运行良好,logcat 中没有显示任何内容。崩溃不容易重现,而且非常不可预测。

我花了将近 2 周的时间使用 ANR 进行搜索和故障排除,但无济于事。 最后同步gradle解决了所有问题......菜鸟错误。

希望这对某人有所帮助。