Android-ANRを調査するにはどうすればよいですか?


153

私のアプリがANR(Application Not Responding)をスローした場所を見つける方法はありますか?/ 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メソッドです。

ありがとう。


2
この種の報告が1つありandroid.os.MessageQueue.nativePollOnce(Native Method)ます。安全に無視できますか?
RDS

回答:


124

ANRは、「メイン」スレッドで長い操作が行われたときに発生します。これはイベントループスレッドであり、ビジー状態の場合、Androidはアプリケーションでこれ以上のGUIイベントを処理できないため、ANRダイアログがスローされます。

さて、あなたが投稿したトレースでは、メインスレッドは問題なく動いているようで、問題はありません。それはMessageQueueでアイドリングしており、別のメッセージが来るのを待っています。あなたの場合、ANRはおそらくスレッドを永続的にブロックするものではなく、より長い操作だったので、操作が終了してイベントスレッドが回復し、トレースが完了しましたANRの後。

ANRが発生する場所を検出することは、それが永続的なブロック(たとえば、いくつかのロックを取得するデッドロック)である場合は簡単ですが、一時的な遅延である場合は困難です。最初に、コードを調べて、脆弱なスポットと長時間実行オペレーションを探します。例としては、イベントスレッド内からのソケット、ロック、スレッドスリープ、およびその他のブロック操作の使用があります。これらがすべて別のスレッドで発生することを確認する必要があります。問題がないようであれば、DDMSを使用してスレッドビューを有効にします。これは、トレースと同様に、アプリケーション内のすべてのスレッドを示します。ANRを再現し、同時にメインスレッドを更新します。ANR時に何が起こっているかを正確に示すはずです


6
唯一の問題は「ANRの再現」です:-)。そのスタックトレースショーのメインスレッドがどのように「アイドリング」であるかを説明していただけますか。
Blundell 2011年

20
スタックトレースは、メインスレッドがルーパー(メッセージループの実装)にあり、Object.waitを介して時間待機を行っていることを示しています。これは、メッセージループには現在ディスパッチするメッセージがなく、新しいメッセージの着信を待機していることを意味します。ANRは、メッセージループがメッセージの処理に多くの時間を費やしており、キュー。ループがメッセージを待機している場合、明らかにこれは発生していません。
2011年

3
@Soonilこんにちはバインダースレッド3、バインダースレッド2 JDWPデーモンプリオ5のようなセクションの残りの意味を知っていますか?sCount、dsCount、obj、sysTid、素敵なschedの意味は何ですか。また、VMWAIT、RUNNABLE、NATIVEなどの情報もあります
minhaz

1
私のアプリはNDKベースですが、同じANRが表示されます。また、メインスレッドは結構です。DDMSを試し、フリーズしたときにワーカースレッドを更新しました。残念ながら、私が取得するのは1行のNativeStart :: runだけです。DDMSスレッドビューは、ネイティブNDKスレッドを検査することもできますか?また、StrictModeは何も見つかりませんでした。
Bram

6
出力の適切な説明については、elliotth.blogspot.com / 2012/08 /…を参照してください。
2013年

96

StrictModeは、APIレベル9以上で有効にできます。

StrictModeは、UI操作が受信されてアニメーションが実行される、アプリケーションのメインスレッド上の偶発的なディスクまたはネットワークアクセスをキャッチするために最も一般的に使用されます。アプリケーションのメインスレッドの応答性を保つことにより、ANRダイアログがユーザーに表示されないようにすることもできます。

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

penaltyLog()使用すると、アプリケーションを使用しながらadb logcatの出力を監視し、違反が発生したときに確認できます。


StrictModeを型に解決できません。最初にインポートする必要があるものはありますか?CTRL + SHIFT + Oを押しても解決しません。
kuchi

23
小さなヒント-if(BuildConfig.DEBUG)...プロダクションに含まれないようにする
Amir Uval

@uval「プロダクションへの組み込みを防止する」とはどういう意味ですか?!!
Muhammed Refaat 2015

2
@MuhammedRefaatそれはANRを妨げません。5秒後ではなく、すぐにアプリがクラッシュします。たとえば、メインスレッドでデータベースにアクセスし、2秒かかる場合、ANRは取得されませんが、StrictModeによってアプリがクラッシュします。StrictModeは、本番環境ではなく、デバッグ段階でのみ使用します。
Amir Uval 2015

1
@MuhammedRefaatが質問への返信を追加しました。
Amir Uval 2015

80

どのタスクがUIスレッドを保持しているのか疑問に思っています。トレースファイルは、タスクを見つけるためのヒントを提供します。各スレッドの状態を調査する必要があります

糸の状態

  • running-アプリケーションコードの実行
  • スリープ-Thread.sleep()を呼び出します
  • モニター-モニターロックの取得を待機しています
  • 待機-Object.wait()内
  • ネイティブ-ネイティブコードの実行
  • vmwait-VMリソースで待機中
  • ゾンビ-スレッドは死にかけている
  • init-スレッドは初期化中です(これは表示されません)
  • 開始-スレッドが開始しようとしています(これも表示されません)

SUSPENDED、MONITOR状態に焦点を当てます。モニター状態はどのスレッドが調査されているかを示し、スレッドのSUSPENDED状態がおそらくデッドロックの主な理由です。

基本的な調査手順

  1. 「ロック待ち」を見つける
    • 監視状態「Binder Thread#15」を見つけることができますprio = 5 tid = 75 MONITOR
    • 「ロック待ち」を見つけたら幸運です
    • 例:threadid = 74によって保持されている<0xblahblah>(com.foo.A)のロックを待機しています
  2. 「tid = 74」がタスクを保持していることがわかります。だからtid = 74に行く
  3. tid = 74おそらくサスペンド状態です!主な理由を見つけてください!

トレースには常に「ロック待ち」が含まれているわけではありません。この場合、主な理由を見つけるのは困難です。


1
いい説明。ANRログを理解するのが簡単になりました。しかし、ステップ1ではスレッドIDを簡単に見つけることができますが、ステップ2でどこに移動しようとしているのか、状態を確認するためにそれを見つけることができないため、理解するのにまだ問題があります。 。それをどのように進めるかアイデアはありますか?
THZ

1
中にい- waiting to lock an unknown objectます"HeapTaskDaemon" daemon prio=5 tid=8 Blocked 。誰かが助けることができるとはどういう意味ですか?
ヒラル

13

私はここ数か月間Androidを学んでいるので、専門家とはほど遠いですが、ANRに関するドキュメントには本当にがっかりしています。

ほとんどのアドバイスは、コードを盲目的に見通すことで回避または修正することを目的としているようですが、トレースを分析しても何も見つかりませんでした。

ANRログで実際に探す必要がある3つの点があります。

1)デッドロック:スレッドがWAIT状態の場合、詳細を調べて、「heldby =」であるユーザーを見つけることができます。ほとんどの場合、それ自体で保持されますが、別のスレッドで保持されている場合は、危険な兆候である可能性があります。そのスレッドを見て、それが何を保持しているかを確認してください。ループが見つかるかもしれません。これは、何か問題が発生したことを示す明らかな兆候です。これは非常にまれですが、それが発生すると悪夢なので、最初のポイントです

2)メインスレッド待機中:メインスレッドがWAIT状態の場合、別のスレッドによって保持されているかどうかを確認します。UIスレッドをバックグラウンドスレッドで保持してはならないため、これは発生しません。

これらのシナリオはどちらも、コードを大幅に作り直す必要があることを意味します。

3)メインスレッドでの負荷の高い操作:これはANRの最も一般的な原因ですが、場合によっては見つけて修正することが難しいものもあります。メインスレッドの詳細を見てください。スタックトレースを下にスクロールし、(アプリから)認識できるクラスが表示されるまでスクロールします。トレース内のメソッドを見て、これらの場所でネットワーク呼び出しやdb呼び出しなどを行っているかどうかを確認します。

最後に、自分のコードを恥ずかしくないように差し込んだことをお詫びします。https://github.com/HarshEvilGeek/Android-Log-Analyzerで書いたpythonログアナライザーを使用できますこれにより、ログファイルが確認され、ANRファイルが開かれます。デッドロック、待機中のメインスレッドの検出、エージェントログでキャッチされていない例外の検出、および比較的読みやすい方法でのすべての出力を画面に出力します。ReadMeファイル(これから追加しようとしています)を読んで、その使用方法を学んでください。先週、それは私にトンを助けました!


4

タイミングの問題を分析しているときは常に、デバッグは役に立ちません。ブレークポイントでアプリをフリーズすると問題が解消されるためです。

あなたの最善の策は、アプリのさまざまなスレッドとコールバックに多くのロギング呼び出し(Log.XXX())を挿入し、遅延がどこにあるかを確認することです。スタックトレースが必要な場合は、新しい例外を作成(インスタンス化するだけ)してログに記録します。


2
スタックトレースが必要な場合の新しい例外の作成に関するアドバイスをありがとう。これはデバッグ時に非常に役立ちます:)
kuchi

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


1

ANRに関する私の問題、多くの作業の結果、スレッドがレイアウトに存在しないリソースを呼び出していることがわかりました。例外を返すのではなく、ANRを取得しました...


それは非常に奇妙です
Nilabja 2017年


0

@Horyun Leeの回答を基に、ANRの調査に役立つ小さなPython スクリプトを書きましたtraces.txt

システムにgraphvizインストールさgrapvhvizれている場合、ANRはグラフィックスとして出力されます。

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

ファイルでANRが検出された場合、PNGは次のように出力されますtraces.txt。より直感的です。

ここに画像の説明を入力してください

traces.txt上記で使用したサンプルファイルは、ここから取得したものです


0

ANRウォッチドッグライブラリを使用して、ANRスタックトレースを高レベルの詳細で正確に追跡およびキャプチャすることを検討してください。その後、それらをクラッシュレポートライブラリに送信できます。私は使用をお勧めしますsetReportMainThreadOnly()このシナリオでするます。アプリにフリーズポイントの致命的でない例外をスローさせるか、ANRが発生したときにアプリを強制終了させることができます。

Google Playデベロッパーコンソールに送信される標準のANRレポートは、正確な問題を特定するのに十分なほど正確ではない場合があることに注意してください。そのため、サードパーティのライブラリが必要です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.