Unity Games での一般的な ANR

Unity ANR はさまざまな理由で発生します。ANR の最も一般的な原因は Android および Unity コンポーネントの不正使用とそれらの通信ミス

WebView

WebView は、ウェブページを表示する Android クラスです。サードパーティ SDK(広告など)が WebView を使用して動的なウェブ コンテンツを表示する UnityPlayerActivity 以外のアクティビティで。ANR は サードパーティソフトウェアの SDK が WebView を不正使用しています。

スタック トレース

スタック トレースは、ANR の原因を理解する最初の手段です。

/data/app/~~p-0ksfCD6bF6Sdq6kpVePg==/com.google.android.webview-5YQZOqKbbqp-uoLY6WYnTw==/base.apk!libmonochrome.so
  at J.N.Mhc_M_H$ (Native method)
  at org.chromium.components.viz.service.frame_sinks.ExternalBeginFrameSourceAndroid.doFrame (chromium-TrichromeWebViewGoogle.aab-stable-579013831:60)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1054)
  at android.view.Choreographer.doCallbacks (Choreographer.java:878)
  at android.view.Choreographer.doFrame (Choreographer.java:807)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1041)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:223)
  at android.app.ActivityThread.main (ActivityThread.java:7721)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)

図 1. futex 待機が原因で発生する ANR スタック トレース

原因

現在のところ、この問題の原因は不明です。考えられる原因によっては、 たとえば、

  • 広告の実装に問題があります。
  • ユーザーが更新しないよう選択した可能性があるため、WebView の古いバージョン 自動的に移行されます。
  • システム リソース(CPU、GPU など)の使用率が高く、大量のリソースが必要になる可能性がある 分析できます
  • シェーダー コンパイルがクラッシュします。これは、 コンテンツに互換性のないシェーダーがあるか、古い WebView を使用している インストールされています。

解決策

  • WebView がコンテンツをブロックする原因となっているコンテンツの種類を絞り込むには、 main スレッドで、ウェブページの読み込み、表示、 閉じることができます。
    • Backtrace または Crashlytics を使用できます。 説明します。
    • データを分析して問題を特定したら、 除外します
    • メモリ関連の問題がないように、メモリログを含めてください。
  • Google Play から WebView を更新するようユーザーに警告します。Android 5.0 以降 (API レベル 21)以降、WebView は APK に移動しました。そのため、 Android プラットフォームとは別に更新されています。WebView のバージョンを確認するには がデバイスで使用中の場合は、[設定] >アプリ >Android システム WebView に移動して、ページの下部にあるバージョンを確認します。
で確認できます。 <ph type="x-smartling-placeholder">
</ph> WebView のバージョンを示すアプリ情報画面。
図 1.WebView のバージョンを確認します。

Unity の一時停止

UnityPlayerActivityonPause() 呼び出しを受信すると、次の一連の処理が行われます。 オペレーションが開始します。

  1. UnityPlayerActivity は、Unity ランタイム エンジンにアクティビティが 一時停止しました。
  2. Unity は、Python や SDK の API を実装するすべての MonoBehaviourOnApplicationPause イベント。
  3. Unity は、音声の再生、レンダリング、 ゲームループ、アニメーションです。
  4. Unity Android Player(UAP)とエンジンの両方が 同期されている場合、UAP はエンジンが停止するまで 4 秒間待機します。
  5. そのオペレーションに 5 秒以上かかると ANR がトリガーされます。

スタック トレース

"main" tid=1 Timed Waiting
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos (AbstractQueuedSynchronizer.java:1079)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos (AbstractQueuedSynchronizer.java:1369)
java.util.concurrent.Semaphore.tryAcquire (Semaphore.java:415)
com.unity3d.player.UnityPlayer.pauseUnity (UnityPlayer.java:833)
com.unity3d.player.UnityPlayer.pause (UnityPlayer.java:796)
com.unity3d.player.UnityPlayerActivity.onPause (UnityPlayerActivity.java:117)
android.app.Activity.performPause (Activity.java:8517)
android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1618)
android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5061)
android.app.ActivityThread.performPauseActivity (ActivityThread.java:5022)
android.app.ActivityThread.handlePauseActivity (ActivityThread.java:4974)
android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:48)
android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:179)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2303)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:201)
android.os.Looper.loop (Looper.java:288)
android.app.ActivityThread.main (ActivityThread.java:7884)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)

図 3. 解放されないセマフォによって発生する ANR。

解決策

C# ゲームコードの実行が完了するまでに時間がかかりすぎないように、 一時停止や再開ができます。

  • ゲームのプロファイリングを行い、OnApplicationPause が高額かどうかを確認する あります。Stopwatch を使用できます。
  • I/O オペレーションや同期ネットワーク リクエストを避ける。
  • 次のコマンドを使用して、オペレーションを別の Thread に移動します。 Task。Unity 2023.1 では、シンプルな C# を使用した非同期プログラミング モデル async キーワードと await キーワード。

UnitySendMessage がブロックされています

Java Unity プラグインと SDK は、JNI を使用して C# ゲームレイヤにデータを送信します。 ただし、この通信はネイティブ レイヤのため、メインスレッドをブロックする 同期ルーチン(ミューテックスなどの同期ルーチン)が実行され、ロック競合による ANR が発生します。

スタック トレース

図 4 の ANR は、C# コードの長いオペレーション Java プラグイン。Unity エンジンが優先度以外の継承を使用する MUTEXを使用して正しく実行されるようにします

libc.so NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*) + 604
com.unity3d.player.UnityPlayer.nativeUnitySendMessage (Native method)
com.unity3d.player.UnityPlayer.UnitySendMessage (UnityPlayer.java:665)

図 4.ロックの競合が原因で発生した ANR。

原因

問題は、アプリケーションが実行時に複数のメッセージがディスパッチされ、 再開されます。メッセージはゲーム中に送信できないため、キューに入れられます。 バックグラウンドで動作しています。メッセージはすべて同時にディスパッチされ、 できます。

一時停止期間中は通常、ゲームの情報を serverたとえば、ゲーム内でのプレーヤーの位置を記録して、 ゲームの再開時に同じ場所に戻ることができます。

このワークロードを、独自のワークロードを作成する他のサードパーティ コードと組み合わせることで、 デバイスのリソース、特にメインスレッドが過負荷状態になる可能性があります。主な アプリのユーザー インターフェースを実行し、多くの場合 ANR の主な発生場所となります。したがって、 メインスレッドにワークロードが追加されると ANR が発生する可能性が高まります。

解決策

アプリの一時停止中に、すべてのコード アクションが必要であることを確認します。または、 ユーザーの状態をローカル デバイスのメモリに保存してみてください。そしてもちろん 一時停止期間外にこれらの操作を完了できるかどうかも確認します。

いくつかのアプローチ:

  • メッセージを処理する C# オペレーションをスレッドに移動する 実行されるようにします。
    • コードが Unity のメインスレッド コンテキストに依存しない場合は、次のコマンドを使用します。 メッセージではなくコミュニケーション用の Task
  • ゲームが一時停止しているときにプラグインから複数のメッセージを送信しないでください。
    • ゲームがバックグラウンドで実行されている間は、エンジンからメッセージを送信できません。
    • ゲームに影響を与えない場合にのみ、最後のデータ状態をゲームに送信する 説明します。

インストール リファラー

Play Install Referrer ユーザーが広告をクリックする。Android 固有の広告トラッキング ID です。1 回 インストールされていれば、アプリはインストール リファラーをアトリビューション パートナーに送信します。 ソースとインストール(コンバージョンのアトリビューション)を照合します。

スタック トレース

図 5 は、Facebook SDK を使用して次のアクションを実行するゲームの ANR スタック トレースを示しています。 インストールのアトリビューションを取得します

図 5. バインダー呼び出しを含む Android Vitals レポート。

原因

ANR の原因は、バインダー呼び出しが遅いことである。しかし、根本原因が アクセスできないことがわかります。

解決策

この種の問題を解決するには、SDK デベロッパーや 解決策を探している多くのユーザーが 他の SDK では ANR を解決したり ロールアウト戦略について説明します。

Google では、使用状況データを組み合わせた SDK Index ページを提供しています。 コード検出によって収集された情報を基に、Google Play アプリから 採用するかどうかを判断するための属性とシグナルを提供します。 SDK を引き続き使用するか、アプリから SDK を削除する。

参考情報

ANR について詳しくは、以下のリソースをご覧ください。