ANR をデバッグする

Unity ゲームでの ANR の解決は体系的なプロセスです。

図 1. Unity ゲームにおける ANR の解決手順

レポート サービスを統合する

Android VitalsFirebase CrashlyticsBacktrace(Unity の認定パートナー)などのレポート サービスでは、大規模なゲームのエラーロギングと分析を利用できます。開発サイクルの早い段階で、レポート サービス SDK をゲームに統合します。ゲームのニーズと予算に最も適したレポート サービスを分析してください。

レポート サービスによって ANR のキャプチャ方法は異なります。2 つ目のレポート サービスを追加して、ANR の修正の判断に役立つ有効なデータを取得する機会を増やします。

レポート SDK を統合しても、ゲームのパフォーマンスや APK のサイズには影響しません。

シンボルを分析する

レポート サービスからのレポートを分析し、スタック トレースが人が読める形式になっているかどうかを確認します。詳しくは、Unity ゲームの Android のクラッシュと ANR をシンボリケートするをご覧ください。

図 2. ビルド ID と欠落している libil2cpp.so シンボルを示す Crashlytics。

シンボルビルド ID の確認方法

レポート システムにビルド ID の欠落が表示されていても、ビルドシンボルがビルドマシンのストレージにまだ存在する場合は、シンボルのビルド ID を確認してから、レポート サービスにアップロードできます。それ以外の場合は、シンボル ファイルをアップロードするために新しいビルドが必要です。

Windows または macOS の場合:

  1. スクリプト バックエンドに基づいてシンボル フォルダに移動します(解決策を参照)。
    1. 次のコマンドを使用します(Windows の場合は、Cygwin を使用して readelf ユーティリティを実行します)。
    2. テキスト出力をフィルタする場合の Grep の使用は任意です。
    3. ビルド ID を探す
readelf -n libil2cpp.so | grep 'Build ID'
Build ID: b42473fb7449e44e0182dd1f580c99bab0cd8a95

ゲームコードを検査する

スタック トレースに libil2cpp.so ライブラリの関数が示されると、C++ に変換された C# コードでエラーが発生しています。libil2cpp.so ライブラリにはゲームコードだけでなく、プラグインとパッケージも含まれます。

C++ のファイル名は、Unity プロジェクトで定義されたアセンブリ名に従います。それ以外の場合、ファイル名にはデフォルトの Assembly-C# 名が使用されます。たとえば図 3 は、アセンブリ定義ファイルで定義されている名前である Game.cpp ファイル(青色でハイライト表示)のエラーを示しています。Logger は、C# スクリプトのクラス名(赤色でハイライト)で、その後に関数名(緑色でハイライト)が続きます。最後は、IL2CPP コンバータが生成したフルネームです(オレンジ色でハイライト表示されています)。

図 3. Backtrace からプロジェクトのコールスタックをテストします。

次の手順でゲームコードを検査します。

  • C# プロジェクトに不審なコードがないかどうかを調べます。通常、C# の未処理の例外によって ANR やアプリケーションのクラッシュは発生しません。それでも、さまざまな状況でコードが適切に実行されることを確認してください。コードでサードパーティのエンジン モジュールが使用されているかどうかを確認し、最近のリリースでエラーが発生していないかどうかを分析します。さらに、Unity を最近更新したのか、特定のデバイスでのみエラーが発生しているのかも確認してください。
  • ゲームを Android Studio プロジェクトとしてエクスポートします。ゲームの変換された C# ソースコードへの完全アクセス権があれば、ANR を引き起こしている関数を特定できます。C++ コードは C# コードと見た目が大きく異なり、コード変換で問題が発生することはほとんどありません。問題が見つかった場合は、Unity にサポート チケットを提出してください。
  • ゲームのソースコードを確認し、OnApplicationFocus() コールバックと OnApplicationPause() コールバックで実行されているロジックが適切にクリーンアップされていることを確認します。
    • Unity エンジンには、実行を一時停止するためのタイムアウトがあります。これらのコールバックのワークロードが過剰になると、ANR が発生する可能性があります。
    • コードの一部にログまたはパンくずリストを追加して、データ分析を強化できます。
  • Unity Profiler を使用してゲームのパフォーマンスを調査します。アプリのプロファイリングは、ANR の原因となっている可能性があるボトルネックを特定するのにも役立ちます。
  • メインスレッドでの長時間の I/O オペレーションを特定するには、厳格モードを使用することをおすすめします。
  • Android Vitals または他のレポート サービス履歴を分析し、エラーが最も頻繁に発生しているゲームのリリース バージョンを確認します。バージョン管理履歴でソースコードを確認し、リリース間でコードの変更を比較します。不審な点が見つかった場合は、変更または修正の可能性を個別にテストしてください。
  • ANR の発生が最も多いデバイスと Android バージョンについて、Google Play ANR レポート履歴を調べます。デバイスやバージョンが古い場合、ゲームの収益性に影響が及ばないのであれば無視してかまいません。特定のユーザー グループがゲームをプレイできなくなるため、データを慎重に確認してください。詳細については、配信ダッシュボードをご覧ください。
  • ゲームのソースコードを確認して、問題を引き起こす可能性のあるコードを呼び出さないようにします。たとえば、finish を正しく使用しないと破壊的になる可能性があります。Android 開発について詳しくは、Android デベロッパー ガイドをご覧ください。
  • データを確認してゲームビルドを Android Studio にエクスポートすると、C / C++ コードを扱います。これにより、Unity の標準ソリューション以外のツール(Android Memory ProfilerAndroid CPU Profilerperfetto など)を最大限に活用できるようになります。

Unity エンジンのコード

ANR が Unity エンジン側で発生しているかどうかを確認するには、スタック トレースで libUnity.so または libMain.so を確認します。見つかった場合は、次の操作を行います。

  • まず、コミュニティ チャンネル(Unity フォーラムUnity ディスカッションStackoverflow)を検索します。
  • 何も見つからない場合は、バグを報告して問題を解決します。エンジンのエンジニアがエラーを詳細に理解して解決できるように、シンボリケートされたスタック トレースを提供します。
  • 最新の Unity LTS で問題に関連する改善が行われているかどうかを確認します。その場合は、そのバージョンを使用するようにゲームをアップグレードしてください。(このソリューションは一部のデベロッパーにしか利用できない場合があります)。
  • デフォルトではなくカスタムの Activity をコードで使用している場合は、Java コードを調べて、アクティビティに問題がないことを確認してください。

サードパーティの SDK

  • すべてのサードパーティ ライブラリが最新であり、最新バージョンの Android でクラッシュや ANR のレポートがないことを確認します。
  • Unity フォーラムにアクセスして、新しいバージョンですでに解決されているエラーがないか、Unity やコミュニティ メンバーによって回避策が提供されているかどうかを確認します。
  • Google Play ANR レポートを調べて、エラーが Google によってまだ特定されていないことを確認します。Google では一部の ANR を認識しており、修正に積極的に取り組んでいます。

システム ライブラリ

システム ライブラリは通常、デベロッパーが制御できる範囲にありますが、ANR の大部分を占めるわけではありません。システム ライブラリ ANR の解決は、ライブラリのデベロッパーに問い合わせるか、ログを追加して問題を絞り込むことだけではありません。

終了理由

ApplicationExitInfo は、ANR の原因を理解するための Android API です。ゲームが Unity 6 以降を使用している場合は、ApplicationExitInfo を直接呼び出すことができます。古いバージョンの Unity の場合、Unity からの ApplicationExitInfo 呼び出しを有効にするには、独自のプラグインを実装する必要があります。

Crashlytics も ApplicationExitInfo を使用します。ただし、独自の実装を使用すると、より細かく制御でき、より関連性の高い情報を含めることができます。