Errores de ANR comunes de juegos de Unity

Los errores de ANR de Unity ocurren por diversos motivos. La mayoría de los errores ANR comunes se deben a el uso inadecuado de los componentes de Android y Unity, así como su mala comunicación.

WebView

WebView es una clase de Android que muestra páginas web. Terceros Los SDK (como los anuncios) usan WebView para mostrar contenido web dinámico. en actividades distintas a UnityPlayerActivity. Los errores de ANR se producen cuando Los SDKs hacen un uso inadecuado de WebView.

Seguimiento de pila

El seguimiento de pila es el primer recurso para comprender la causa del error de 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)

Figura 1: Seguimiento de pila de ANR causado por una espera de futex

Causa

Hasta ahora, la causa raíz de este problema no es clara. Algunas causas potenciales pueden incluyen:

  • Mala implementación del anuncio.
  • Una versión desactualizada de WebView, ya que es posible que el usuario haya optado por no actualizarla la aplicación automáticamente.
  • Uso elevado de recursos del sistema (CPU, GPU, etc.), lo que puede requerir mucha la generación de perfiles.
  • La compilación de sombreadores falla, lo que podría indicar que la compilación el contenido tiene un sombreador incompatible o el usuario tiene un WebView antiguo versión instalada.

Solución

  • Para acotar el tipo de contenido que hace que WebView bloquee el subproceso principal, agrega registros a tu juego cada vez que se cargue, muestre o cerrado.
    • Puedes usar Backtrace o Crashlytics los servicios de generación de informes.
    • Luego, después de analizar los datos y encontrar el problema, intenta inhabilitar el proveedores de anuncios infractores.
    • Incluye registros de memoria para asegurarte de que el problema no esté relacionado con la memoria.
  • Alerta al usuario para que actualice el WebView desde Google Play. Desde Android 5.0 (nivel de API 21) y versiones posteriores, se movió WebView a un APK. Por lo tanto, puede ser se actualiza por separado de la plataforma de Android. Para ver la versión de WebView se está usando en un dispositivo, ve a Configuración > Aplicaciones > Sistema Android WebView y observa la versión que se encuentra en la parte inferior de la página.
Pantalla de información de la app que muestra las versiones de WebView.
Figura 1: Verifica la versión de WebView.

Pausa de Unity

Cuando UnityPlayerActivity recibe una llamada a onPause(), la siguiente cadena de de inicio de las operaciones:

  1. UnityPlayerActivity notifica al motor de tiempo de ejecución de Unity que la actividad se pausó.
  2. Unity llama a cada MonoBehaviour que implemente el OnApplicationPause evento.
  3. Unity detiene sus componentes y módulos, como la reproducción de sonido, la renderización, bucle de juego y animación.
  4. Para asegurarte de que tanto el Unity Android Player (UAP) como el motor el UAP espera 4 segundos para que se detenga el motor.
  5. Si esa operación tarda más de 5 segundos, el sistema activa un error de ANR.

Seguimiento de pila

"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)

Figura 3: ANR causado por un semáforo que nunca se liberó.

Solución

Asegúrate de que el código de tu juego de C# no tarde demasiado en terminar de ejecutarse durante una pausar o reanudar el evento.

  • Genera un perfil de tu juego y comprueba si el OnApplicationPause es costoso una sola operación. Puedes usar un objeto Stopwatch.
  • Evita las operaciones de E/S o las solicitudes de red síncronas.
  • Mueve las operaciones a otro Thread con el elemento Task Unity 2023.1 admite una versión modelo de programación asíncrono con C# Palabras clave async y await.

Se bloqueó UnitySendMessage

Los complementos y SDK de Java Unity envían datos a la capa de juego de C# mediante JNI. Sin embargo, esta comunicación podría bloquear el subproceso principal debido a un como una exclusión mutua que causa un error de ANR debido a la contención de bloqueo.

Seguimiento de pila

El error de ANR que aparece en la figura 4 se debió a una operación larga en el código C# que llamaba un complemento de Java. El motor de Unity usa una herencia no prioritaria exclusión mutua para garantizar una ejecución correcta.

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)

Figura 4: ANR debido a una contención de bloqueo.

Causa

El problema es que se envían varios mensajes cuando la aplicación se reanude. Los mensajes están en cola porque no se pueden enviar mientras el juego. está en segundo plano. Todos los mensajes se envían simultáneamente cuando el reanuda la app.

Durante un período de pausa, por lo general, almacenas la información del juego en la servidor; Por ejemplo, registras la posición de un jugador en el juego para que este puede volver al mismo lugar cuando se reanude el juego.

Esta carga de trabajo, combinada con otro código de terceros, que crea su propia carga de trabajo puede sobrecargar los recursos del dispositivo, en especial el subproceso principal. El principal ejecuta la interfaz de usuario de una app y suele ser la ubicación principal de los errores de ANR. Entonces: y cualquier carga de trabajo adicional en el subproceso principal aumenta la posibilidad de un error de ANR.

Solución

Durante una pausa de la aplicación, asegúrate de que todas las acciones de código sean necesarias. intenta guardar el estado del usuario en la memoria local de tu dispositivo. Y, por supuesto, observen si también puede completar estas acciones fuera del período de pausa.

Algunos enfoques:

  • Mover la operación C# que controla un mensaje a una conversación diferente del subproceso principal.
    • Si tu código no depende del contexto del subproceso principal de Unity, usa Task para la comunicación en lugar de la mensajería.
  • No envíes varios mensajes desde tu complemento cuando el juego esté en pausa.
    • El motor no puede enviar mensajes mientras el juego está en segundo plano.
    • Solo envía el último estado de datos a tu juego si el problema no se ve afectado funcionalidad.

Referencia de instalación

La referencia de instalación de Play es una cadena única que se envía a Play Store cuando se el usuario hace clic en un anuncio. Es un identificador de seguimiento de anuncios específico de Android. Una vez instalada, la app envía la URL de referencia de instalación al socio de atribución, que establece coincidencias entre la fuente y la instalación (atribución de la conversión).

Seguimiento de pila

En la Figura 5, se muestra un seguimiento de pila de ANR de un juego que usa el SDK de Facebook para recuperar la atribución de instalación.

Figura 5: Informe de Android vitals que contiene una llamada de Binder.

Causa

El error de ANR se debió a una llamada lenta a Binder. Sin embargo, la causa raíz no puede sin acceso al código fuente del SDK.

Solución

Resolver este tipo de problema implica comunicarse con el desarrollador del SDK o con un mucho la búsqueda en línea de una posible solución, comprobar si una nueva del SDK resuelve el error de ANR para otros usuarios o, incluso, si experimenta con un estrategia de lanzamiento.

Google proporciona una página del Índice SDK que combina datos de uso. desde apps de Google Play con información recopilada a través de la detección de códigos proporcionan indicadores y atributos diseñados para decidir si adoptar o no conservar o quitar el SDK de la app.

Recursos adicionales

Para obtener más información sobre los errores de ANR, consulta los siguientes recursos: