【问题标题】:General guide to diagnose ANR [duplicate]诊断 ANR 的一般指南 [重复]
【发布时间】:2015-05-10 09:15:25
【问题描述】:

有很多关于 ANR 跟踪文件的问题,答案总是“哦,问题出在你的线程 76,修复你的 http 调用”之类的 :) 但我找不到任何关于如何阅读的一般指南或教程这一步一步地跟踪任何 ANR。有没有?我特别有几个问题:

  1. 总是可以从我在谷歌控制台中看到的真实世界 ANR 的线程跟踪中看到问题吗?或者如果我无法在本地重现 ANR,是否可能没有相关信息,我运气不好?

  2. 此信息中包含哪些线程?我想我的应用程序进程中有所有线程,但是其余的呢?它们在某种程度上都与我相关吗? (例如我的一些线程正在等待的线程等)或者还有完全不相关的进程?

  3. Google Play 控制台如何确定 ANR 发生的“地点” - 然后显示在 ANR 列表中,例如:

ANR keyDispatchingTimedOut

miesto:com.sample.myapp/myapp.activities.SplashActivity

因为在提供的线程跟踪文本中看不到 SplashActivity。

  1. 我知道我应该寻找潜在的死锁等处于等待状态的线程。线程“等待自己”的情况如何?

“AsyncTask #1”prio=5 tid=15 WAIT | group="main" sCount=1 dsCount=0 obj=0x41bb50c0 自我=0x5529a868 | sysTid=2448 nice=0 sched=0/0 cgrp=应用句柄=1429609576 |状态=S schedstat=( 18097077 39273309 41 ) utm=1 stm=0 core=1 at java.lang.Object.wait(Native Method) - 等待 tid=15 (AsyncTask #1) 持有的 (a java.lang.VMThread)

这是否总是可以的,我可以假设这不是原因?情况如何,我在 NATIVE 中只有一堆线程(包括主线程),而在 WAIT 中只有一堆线程像这样等待自己?这怎么可能是 ANR?

【问题讨论】:

  • ANR 通常并不意味着它没有响应。这通常意味着本机代码发生了崩溃。至于调试它 - 您需要在日志文件中找到堆栈跟踪或核心转储,然后从那里开始工作。活动中每个线程的转储几乎总是一个红鲱鱼,不值得一看,我想我从来没有见过有帮助的。
  • 加布,你确定吗?据我所知,本机崩溃显示在谷歌控制台的另一个选项卡中,并具有“堆栈跟踪”(通常不是很有帮助,但仍然)。您能否指出您声称 ANR 可能是“隐藏”本机崩溃的任何来源?
  • 只是体验。我已经解决了数百个 ANR。我认为其中不超过 1 或 2 个是死锁。
  • 根据我的经验,ANR 是由于主 UI 线程负载过重,而不是本机代码崩溃。甚至谷歌也这么说here。考虑到您的麻烦类被称为“SplashActivity”并且“Splash”屏幕通常用于加载资源,我建议考虑将该 Activity 中的一些任务移动到另一个后台线程。如果它继续存在,请尝试计算完成活动所需的时间,特别是如果您有任何可能需要一些时间来遍历的循环。

标签: android android-anr-dialog


【解决方案1】:

系统向您的应用程序发送各种事件,这些事件在 UI 线程上接收。如果该线程在一定时间内没有响应事件,系统会断定应用程序没有响应,并启动 ANR 处理。

逐点解决您的问题:

  1. 在堆栈跟踪中并不总是可以看到问题。系统服务器进程检测到存在问题,然后向有问题的进程发出信号以转储其堆栈跟踪。如果应用在问题发现和堆栈转储信号之间恢复,那么跟踪不会告诉你太多。

  2. 您应该会看到您的应用程序中的所有线程,并且只能看到您的应用程序。 ANR 机制不会尝试确定一组“相关”线程。开始的地方是 UI 线程,通常是应用程序的“主”线程,看看你是否在被卡住的行为中发现了它。有时应用程序很慢,而不是卡住,而导致缓慢的原因实际上是一个不同的进程正在占用 CPU 或磁盘带宽,但是您在堆栈跟踪中看不到这一点……而且您可能会得到一个堆栈反映执行超过“卡住”点的跟踪。

  3. “地点”是未响应的事件(在本例中为关键事件),以及系统尝试与之交互的 Activity。

  4. 这很正常;你会看到当一个线程通过 Dalvik 中的java.util.concurrent.locks.LockSupport.park()“停放”时。请记住,锁在线程等待时被释放,所以在这种情况下,它只是在等待另一个线程出现并通知它。

解决 cmets 中提出的一个问题:如果 (1) 本机崩溃没有完全杀死应用程序,本机崩溃可能会导致 ANR,这是它应该做的; (2) 死掉的线程是 UI 线程,或者持有 UI 线程正在等待的资源。如果您无法访问完整的 logcat,您可以检查线程列表以确认您的所有线程都处于活动状态。

查看 ANR 时,您首先需要弄清楚它是永久卡住还是只是暂时变慢。这对于使用该应用程序的人来说应该是显而易见的。永久冻结通常是最容易解决的,因为堆栈跟踪通常会引导您找到问题所在。从 UI 线程开始,遍历跟踪,直到找到一些正在旋转或卡在本机调用中的代码。 (虽然本机调用有一个技巧——如果它说 NATIVE 那么它仍然在本机代码中,但是如果它说 SUSPENDED 在堆栈顶部具有本机方法的线程上,那么它不会被卡住,而是在行动中从本机代码返回到托管代码。)

瞬态 ANR 可能更难,尤其是当它们发生在配置未知的客户设备上时。如果他们在由于闪存部件故障而停止的设备上在后台运行 CPU 基准测试,那么您的应用程序将会很糟糕。有时堆栈跟踪会将您指向问题的大致方向(例如,this one,看起来缓慢的渲染和粗略的锁定正在停止 UI 线程),其他时候跟踪是在应用程序恢复正常运行后捕获的。

【讨论】:

  • “从 UI 线程开始并遍历跟踪,直到找到一些正在旋转或卡在本机调用中的代码”你能详细说明一下吗?主线程卡在原生调用中怎么办?
【解决方案2】:

这可能不是检测您正在寻找的 ANR 的总体通用方法,但一个好的开始是为您的应用程序启用严格模式。

您将能够检查 logcat,系统会在您做错了什么时通知您。

只需将这些行添加到您的应用程序或活动的 onCreate() 方法中:

if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectAll()
                .penaltyLog()
                .build());

        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects()
                .detectLeakedClosableObjects()
                .penaltyLog()
                .build());
    }

更多详情:http://developer.android.com/reference/android/os/StrictMode.html

【讨论】:

  • StrictMode 很好也很有用,但我的问题是关于生产中的 ANR,您无法在本地复制它们。
  • 即使你不能复制它们本身,就像看到它们一样,严格模式会记录你在 UI 线程上所做的一切,所以你应该能够在日志中看到它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
  • 2019-02-24
  • 1970-01-01
  • 2011-12-29
  • 2012-05-07
  • 1970-01-01
相关资源
最近更新 更多