web-dev-qa-db-ja.com

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

私のアプリが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メソッドです。

ありがとう。

143
lostInTransit

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

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

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

118
sooniln

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

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

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

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

93
Dheeraj V.S.

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

スレッドの状態

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

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

基本的な調査手順

  1. 「ロック待ち」を探します
    • モニターの状態を見つけることができます"Binder Thread#15" prio = 5 tid = 75 MONITOR
    • 「ロック待ち」を見つけたらラッキーです
    • 例:threadid = 74が保持する<0xblahblah>(com.foo.A)のロックを待機
  2. 「tid = 74」がタスクを保持していることがわかります。 tid = 74に移動します
  3. tid = 74は一時停止状態かもしれません!主な理由を見つけてください!

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

68
Horyun Lee

私はここ数ヶ月間Androidを学んできたので、専門家とはほど遠いですが、ANRのドキュメントには本当に失望しています。

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

ANRログで本当に探す必要がある3つのことがあります。

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

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

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

3)メインスレッドでのヘビーオペレーション:これはANRの最も一般的な原因ですが、時には発見および修正が難しいものの1つです。メインスレッドの詳細を確認します。スタックトレースをスクロールダウンし、(アプリから)認識しているクラスが表示されるまで。トレース内のメソッドを見て、これらの場所でネットワーク呼び出し、db呼び出しなどを行っているかどうかを判断します。

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

タイミングの問題を分析するときはいつでも、デバッグが役に立たないことがよくあります。ブレークポイントでアプリをフリーズすると問題がなくなるためです。

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

4
Ulrich

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

3
Jack

/ data/anr/traces.txtファイルで「ロック待ち」を探す必要があります。

enter image description here

詳細については、 Android&Play(Google I/O '17)のツールを使用した高パフォーマンスのエンジニア

1
phnmnn

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

1
yaniv

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

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

0
Mr-IDE

@Horyun Leeの回答の基本で、traces.txtからANRを調査するために、小さなpython script を作成しました。

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

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

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

enter image description here

上記で使用したサンプルtraces.txtファイルは、 here から取得しました。

0
alijandro