Ошибки ANR в Unity возникают по разным причинам. Чаще всего они вызваны неправильным использованием компонентов Android и Unity, а также их несовместимостью.
WebView
WebView
— это класс Android, предназначенный для отображения веб-страниц. Сторонние SDK (например, рекламные) используют WebView
для отображения динамического веб-контента в активностях, отличных от UnityPlayerActivity
. Ошибки ANR возникают при неправильном использовании WebView
сторонними SDK.
Трассировка стека
Трассировка стека — это ваш первый способ понять причину возникновения 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. Трассировка стека ANR, вызванная ожиданием фьютекса .
Причина
На данный момент истинная причина этой проблемы неясна. Возможные причины:
- Плохая реализация рекламы.
- Устаревшая версия
WebView
, поскольку пользователь мог отказаться от автоматического обновления приложения. - Высокое использование системных ресурсов (ЦП, ГП и т. д.), что может потребовать тщательного профилирования.
- Происходит сбой компиляции шейдера , что может указывать на то, что контент имеет несовместимый шейдер или что у пользователя установлена старая версия
WebView
.
Решение
- Чтобы сузить круг типов контента, из-за которых
WebView
блокирует основной поток, добавьте в игру журналы каждой загрузки, отображения или закрытия веб-страницы.- Вы можете воспользоваться службами отчетности Backtrace или Crashlytics .
- Затем, проанализировав данные и обнаружив проблему, попробуйте отключить нарушающих работу поставщиков рекламы.
- Приложите журналы памяти, чтобы убедиться, что проблема не связана с памятью.
- Предупредите пользователя о необходимости обновления
WebView
из Google Play . Начиная с Android 5.0 (API уровня 21) и выше,WebView
перешёл в формат APK. Поэтому его можно обновлять отдельно от платформы Android. Чтобы узнать, какая версияWebView
используется на устройстве, перейдите в раздел «Настройки�� > «Приложения» > «Системный WebView Android» и посмотрите версию внизу страницы.

WebView
.Пауза единства
Когда UnityPlayerActivity
получает вызов onPause()
, запускается следующая цепочка операций:
-
UnityPlayerActivity
уведомляет движок среды выполнения Unity о том, что действие приостановлено. - Unity вызывает каждый
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
. - ��збегайте операций ввода-вывода или синхронных сетевых запросов.
- Пер��н��с��те ��пераци�� в другой
Thread
с помощьюTask
. Unity 2023.1 поддерживает упрощенную модель асинхронного программирования с использованием ключевых слов C#async
иawait
.
UnitySendMessage заблокирован
Плагины и SDK Java Unity отправляют данные в игровой слой C# с помощью JNI . Однако это взаимодействие может блокировать основной поток из-за встроенной процедуры синхронизации, например, мьютекса, что может привести к ошибке ANR из-за конфликта блокировок.
Трассировка стека
Ошибка ANR на рисунке 4 была вызвана длительной операцией в коде C#, вызванной плагином Java. Движок Unity использует мьютекс Non-Priority Inheritance для обеспечения корректного выполнения.
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, вызванная конфликтом блокировок.
Причина
Проблема заключается в том, что при возобновлении работы приложения отправляется несколько сообщений. Сообщения ставятся в очередь, поскольку их отправка невозможна, пока игра находится в фоновом режиме. При возобновлении работы приложения все сообщения отправляются одновременно.
Во время периода паузы вы обычно сохраняете игровую информацию на сервере; например, вы записываете позицию игрока в игре, чтобы игрок мог вернуться в то же место, когда игра возобновится.
Эта нагрузка в сочетании с другим сторонним кодом, создающим собственную нагрузку, может перегружать ресурсы устройства, особенно основной поток. Основной поток отвечает з�� работу пользовательского интерфейса приложения и часто является основным источником ошибок ANR. Таким образом, любая дополнительная нагрузка на основной поток увеличивает вероятность возникновения ошибки ANR.
Решение
Во время приостановки приложения убедитесь, что все действия в коде необходимы, или попробуйте сохранить состояние пользователя в локальной памяти устройства. И, конечно же, проверьте, сможете ли вы выполнить эти действия и вне периода приостановки.
Несколько подходов :
- Переместите операцию C#, обрабатывающую сообщение , в поток, отличный от основного потока.
- Если ваш код не зависит от контекста основного потока Unity, используйте для связи
Task
вместо message.
- Если ваш код не зависит от контекста основного потока Unity, используйте для связи
- Не отправляйте несколько сообщений из плагина, когда игра приостановлена.
- Движок не может отправлять сообщения, пока игра находится в фоновом режиме.
- Отправляйте в игру последнее состояние данных только в том случае, если это не повлияет на функциональность игры.
Установить реферер
Реферер установки Play — это уникальная строка, отправляемая в Play Store каждый раз, когда пользователь нажимает на объявление. Это идентификатор отслеживания рекламы, специфичный для Android. После установки приложение отправляет реферер установки партнёру по атрибуции, который сопоставляет источник с установкой (атрибуция конверсии).
Трассировка стека
На рисунке 5 показана трассировка стека ANR из игры, которая использует Facebook SDK для получения атрибуции установки.

Причина
Ошибка ANR была вызвана медленным вызовом связующего компонента. Однако определить первопричину без доступа к исходному коду SDK невозможно.
Решение
Решение такого типа проблем подразумевает общение с разработчиком SDK или долгий поиск потенциального решения в Интернете, проверку того, решает ли новая версия SDK проблему ANR ��ля других, или даже экспериментирование с небольшой стратегией развертывания.
Google предоставляет страницу индекса SDK , которая объединяет данные об использовании приложений Google Play с информацией, собранной с помощью обнаружения кода, чтобы предоставить атрибуты и сигналы, призванные помочь вам решить, следует ли внедр��ть, сохранят�� ��ли у��а��ять SDK из вашего приложения.
Дополнительные ресурсы
Чтобы узнать больше об ANR, обратитесь к следующим ресурсам:
- Отладка ANR — разработка игр для Android
- ANR — Качество приложения