167

有没有办法找出我的应用程序在哪里抛出了 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 -----

我怎样才能找出问题出在哪里?trace 中的方法都是 SDK 方法。

4

12 回答 12

134

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

现在,在您发布的跟踪中,主线程似乎运行良好,没有问题。它在 MessageQueue 中空闲,等待另一条消息进来。在您的情况下,ANR 可能是一个更长的操作,而不是永久阻塞线程的东西,因此事件线程在操作完成后恢复,并且您的跟踪通过了在 ANR 之后。

如果 ANR 是永久阻塞(例如死锁获取一些锁),则检测 ANR 发生的位置很容易,但如果只是暂时的延迟,则更难。首先,检查您的代码并寻找易受攻击的地方和长时间运行的操作。示例可能包括在事件线程内使用套接字、锁、线程休眠和其他阻塞操作。您应该确保这些都发生在单独的线程中。如果没有问题,请使用 DDMS 并启用线程视图。这将显示应用程序中的所有线程,类似于您拥有的跟踪。重现ANR,同时刷新主线程。这应该准确地向您展示 ANR 时发生的事情

于 2009-04-01T20:59:06.267 回答
107

您可以在 API 级别 9 及更高级别启用StrictMode 。

StrictMode 最常用于捕获应用程序主线程上的意外磁盘或网络访问,其中接收 UI 操作并发生动画。通过保持应用程序的主线程响应,您还可以防止向用户显示ANR 对话框。

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

使用penaltyLog()您可以查看 adb logcat 的输出,同时使用您的应用程序查看发生的违规行为。

于 2011-07-29T09:00:19.987 回答
84

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

线程状态

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

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

基本调查步骤

  1. 找到“等待锁定”
    • 你可以找到监控状态“Binder Thread #15”prio=5 tid=75 MONITOR
    • 如果发现“等待锁定”你很幸运
    • 示例:等待锁定 threadid=74 持有的 <0xblahblah> (a com.foo.A)
  2. 您可以注意到“tid=74”现在持有一个任务。所以去 tid=74
  3. tid=74 可能是 SUSPENDED 状态!找到主要原因!

跟踪并不总是包含“等待锁定”。在这种情况下,很难找到主要原因。

于 2014-07-15T15:03:37.917 回答
15

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

大多数建议似乎都是为了避免它们或通过盲目地查看代码来修复它们,这很好,但我在分析跟踪时找不到任何东西。

对于 ANR 日志,您确实需要寻找三件事。

1)死锁:当一个线程处于WAIT状态时,你可以通过查看细节来找到它的“heldby=”。大多数时候,它会被自己持有,但如果它被另一个线程持有,那很可能是一个危险信号。去看看那个线程,看看它是由什么持有的。您可能会发现一个循环,这是出现问题的明显迹象。这是非常罕见的,但这是第一点,因为当它发生时,这是一场噩梦

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

这两种情况都意味着您需要大量修改代码。

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

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

于 2014-08-08T07:14:57.177 回答
4

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

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

于 2011-02-18T20:26:54.617 回答
3

什么触发 ANR?

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

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

如何避免 ANR

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

因此,在 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

于 2014-04-25T09:14:13.227 回答
2

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

在此处输入图像描述

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

于 2017-05-18T07:27:50.547 回答
1

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

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

希望这会对某人有所帮助。

于 2021-05-07T06:01:14.867 回答
0

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

于 2017-02-06T08:15:53.427 回答
0

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

graphviz如果您已在系统上安装, ANR 将作为图形输出grapvhviz

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

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

在此处输入图像描述

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

于 2017-07-11T10:10:22.087 回答
0

考虑使用ANR-Watchdog库来准确跟踪和捕获 ANR 堆栈跟踪的详细信息。然后,您可以将它们发送到您的崩溃报告库。我建议setReportMainThreadOnly()在这种情况下使用。您可以让应用程序抛出一个非致命的冻结点异常,或者让应用程序在 ANR 发生时强制退出。

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

于 2017-08-07T09:38:16.303 回答
0

对于已发布的应用程序,Google Play 控制台本身会显示 Exact ANR 报告,如崩溃报告。它将向您显示导致 ANR 的类或线程的所有信息、发生次数和所有其他详细信息。

您的应用仪表板-> 探索 Android Vitals 选项卡 -> 查看核心 Vitals 详细信息(ANR 部分)-> 选择您的 apk 或工件 vesrion -> 崩溃和 ANR 页面只需从下拉列表中选择 ANR。它将列出所有 ANR。

您可以导航到特定的 ANR 并查看详细信息。

于 2021-08-26T05:56:44.887 回答