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 deWebView
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.
Pausa de Unity
Cuando UnityPlayerActivity
recibe una llamada a onPause()
, la siguiente cadena de
de inicio de las operaciones:
UnityPlayerActivity
notifica al motor de tiempo de ejecución de Unity que la actividad se pausó.- Unity llama a cada
MonoBehaviour
que implemente elOnApplicationPause
evento. - Unity detiene sus componentes y módulos, como la reproducción de sonido, la renderización, bucle de juego y animación.
- Para asegurarte de que tanto el
Unity Android Player
(UAP) como el motor el UAP espera 4 segundos para que se detenga el motor. - 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 objetoStopwatch
. - Evita las operaciones de E/S o las solicitudes de red síncronas.
- Mueve las operaciones a otro
Thread
con el elementoTask
Unity 2023.1 admite una versión modelo de programación asíncrono con C# Palabras claveasync
yawait
.
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.
- Si tu código no depende del contexto del subproceso principal de Unity, usa
- 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.
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:
- Depura errores de ANR: Desarrollo de juegos para Android
- ANR: Calidad de la app