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 に移動して、ページの下部にあるバージョンを確認します。
Unity の一時停止
UnityPlayerActivity
が onPause()
呼び出しを受信すると、次の一連の処理が行われます。
オペレーションが開始します。
UnityPlayerActivity
は、Unity ランタイム エンジンにアクティビティが 一時停止しました。- Unity は、Python や SDK の API を実装するすべての
MonoBehaviour
をOnApplicationPause
イベント。 - Unity は、音声の再生、レンダリング、 ゲームループ、アニメーションです。
Unity Android Player
(UAP)とエンジンの両方が 同期されている場合、UAP はエンジンが停止するまで 4 秒間待機します。- そのオペレーションに 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
。
- コードが Unity のメインスレッド コンテキストに依存しない場合は、次のコマンドを使用します。
メッセージではなくコミュニケーション用の
- ゲームが一時停止しているときにプラグインから複数のメッセージを送信しないでください。
- ゲームがバックグラウンドで実行されている間は、エンジンからメッセージを送信できません。
- ゲームに影響を与えない場合にのみ、最後のデータ状態をゲームに送信する 説明します。
インストール リファラー
Play Install Referrer ユーザーが広告をクリックする。Android 固有の広告トラッキング ID です。1 回 インストールされていれば、アプリはインストール リファラーをアトリビューション パートナーに送信します。 ソースとインストール(コンバージョンのアトリビューション)を照合します。
スタック トレース
図 5 は、Facebook SDK を使用して次のアクションを実行するゲームの ANR スタック トレースを示しています。 インストールのアトリビューションを取得します
原因
ANR の原因は、バインダー呼び出しが遅いことである。しかし、根本原因が アクセスできないことがわかります。
解決策
この種の問題を解決するには、SDK デベロッパーや 解決策を探している多くのユーザーが 他の SDK では ANR を解決したり ロールアウト戦略について説明します。
Google では、使用状況データを組み合わせた SDK Index ページを提供しています。 コード検出によって収集された情報を基に、Google Play アプリから 採用するかどうかを判断するための属性とシグナルを提供します。 SDK を引き続き使用するか、アプリから SDK を削除する。
参考情報
ANR について詳しくは、以下のリソースをご覧ください。