Android-我如何调查一个 ANR?

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

161718 次浏览

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

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

如果是永久块(例如,死锁获取某些锁) ,那么检测 ANR 发生的位置很容易,但如果只是暂时延迟,则更难。首先,检查您的代码并查找易受攻击的地方和长时间运行的操作。示例可能包括在事件线程中使用套接字、锁、线程睡眠和其他阻塞操作。您应该确保这些都在不同的线程中发生。如果没有问题,使用 DDMS 并启用线程视图。这将显示应用程序中类似于跟踪的所有线程。复制 ANR,同时刷新主线程。这应该可以精确地显示在 ANR 的时候发生了什么

无论何时分析计时问题,调试通常都没有帮助,因为将应用程序冻结在断点会使问题消失。

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

您可以在 API 级别9及以上启用 StrictMode

StrictMode 最常用于捕获意外磁盘或网络 访问应用程序的主线程,其中的 UI 操作 通过保持应用程序的主线程 响应,您也从 防止 ANR 对话框显示给用户。

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

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

什么触发器 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");
}
}

代码: 执行 Worker 线程

要执行这个辅助线程,只需创建一个实例并调用 execute () :

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

来源

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

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

线索状态

  • 运行-执行的应用程序代码
  • Sleep-称为 Thread.sleep ()
  • 监视器-等待获取监视器锁
  • Wait-in Object.wait ()
  • 本机执行的本机代码
  • 等待 VM 资源
  • 僵尸线程正在死亡的过程中
  • Init-thread 正在初始化(您不应该看到这一点)
  • Start-thread 即将启动(您也不应该看到这一点)

重点关注暂停状态,监控状态。监视器状态指示调查哪个线程,线程的 SUSPENDED 状态可能是导致死锁的主要原因。

基本调查步骤

  1. 查找“等待锁定”
    • 您可以找到监视器状态 “装订线 # 15”prio = 5 tid = 75 MONITOR
    • 如果你发现“等待锁定”
    • 示例: 等待锁定由 threadid = 74持有的 < 0xblahbia > (a com.foo.A)
  2. 您可以注意到“ tid = 74”现在保存了一个任务,因此转到 tid = 74
  3. Tid = 74也许暂停状态! 找到主要原因!

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

过去几个月我一直在学习机器人,所以我远不是一个专家,但是我对 ANR 的文档真的很失望。

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

使用 ANR 日志时,有三件事情您确实需要查找。

1)死锁: 当一个线程处于 WAIT 状态时,您可以通过查看细节来找到它的“ heldby =”。大多数情况下,它会被自己抓住,但如果它被另一根线抓住,这可能是一个危险的信号。去看看那根线,看看它是用什么固定的。你可能会发现一个循环,这是一个明显的迹象,有什么地方出错了。这很罕见,但这是第一点,因为一旦发生,就是噩梦

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

这两种情况都意味着您需要显著地重新编写代码。

3)主线程上的大量操作: 这是产生 ANR 的最常见原因,但有时也是很难找到和修复的原因之一。查看主线程的细节。向下滚动堆栈跟踪,直到看到(从应用程序中)可以识别的类。查看跟踪中的方法,并确定是否在这些地方进行网络调用、 db 调用等。

最后,我为无耻地插入我自己的代码而道歉,你可以使用我在 https://github.com/HarshEvilGeek/Android-Log-Analyzer上写的 python 日志分析器。它可以检查你的日志文件,打开 ANR 文件,找到死锁,找到等待的主线程,在你的代理日志中找到未捕获的异常,然后以一种相对容易阅读的方式在屏幕上打印出来。阅读 ReadMe 文件(我将要添加它)来学习如何使用它。上周帮了我大忙!

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

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

enter image description here

详情请浏览: Android & Play 工具高性能工程师(Google I/O’17)

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

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

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

如果在文件 traces.txt中检测到 ANR,png 将会输出如下。

enter image description here

上面使用的示例 traces.txt文件是从 给你获得的。

考虑使用 看门狗库精确地跟踪和捕获 ANR 堆栈跟踪。然后您可以将它们发送到崩溃报告库。我建议在这个场景中使用 setReportMainThreadOnly()。您可以使应用程序抛出一个非致命的冻结点异常,或使应用程序强制退出时,ANR 发生。

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

不知道这是否有用。我的问题是应用程序崩溃并冻结了我的设备,然后迫使它在 android 10的设备上重新启动,但在 android 6上运行良好,logcat 中没有显示任何东西。这些崩溃不容易重现,而且非常不可预测。

我花了近2个星期的搜索和故障排除与 ANR,但没有用。 终于同步了所有的问题... . . 新手的错误。

希望这能帮到别人。

对于已发布的应用程序,谷歌播放控制台本身显示精确的 ANR 报告像崩溃报告。它会显示你所有的信息,哪个类或线程正在导致 ANR,发生和所有其他细节。

你的应用程序仪表板-> 浏览 Android 重要部分标签-> 查看核心重要部分细节(ANR 部分)-> 选择你的 apk 或工件 vesrion-> 崩溃和 ANR 页面只需从下拉菜单中选择 ANR。它会列出所有的全国通告。

您可以定位到特定的 ANR 并检查详细信息。

在搜索了许多解决 Android 上 ANR 崩溃的方案之后,我偶然发现了这个链接:

Https://developer.android.com/studio/debug/bug-report

  1. 将你的 Android 设备连接到安装了 ADB 的电脑上

  2. 终端打开

  3. 运行以下命令(将“ path”替换为保存 zip 文件夹的位置,例如 ~/file/report) :

    Adb bugreport

这使我能够访问所有的 ANR (ANR * 文件)错误报告,给我一个完整的回溯文件夹下:

FS-> data-> anr-> anr * files