Figura 1: Diálogo de ANR que se le muestra al usuario
En este documento, se describe la manera en que el sistema Android determina si una app no responde y se muestra cómo mantener la capacidad de respuesta de tu app.
Sin importar qué tan bien escrito esté el código, es posible que tu app se vea lenta, se bloquee o tarde demasiado tiempo en procesar los datos de entrada. Si tu app está en primer plano y no responde, el usuario verá un diálogo de Aplicación no responde (ANR), como se muestra en la figura 1. El diálogo de ANR permite que el usuario fuerce el cierre de la app. Si la app no está en primer plano, se detiene en silencio. Es fundamental diseñar la capacidad de respuesta de tu app para minimizar los diálogos de ANR.
Activadores de ANR
Por lo general, el sistema muestra un error de ANR si una app no puede responder a la entrada del usuario en el subproceso principal (también conocido como subproceso de IU), lo que impide que el sistema procese estos eventos de entrada.
Por ejemplo, puede producirse un error de ANR si una app realiza una operación de bloqueo de E/S, como el acceso a la red, en el subproceso de IU. Otro ejemplo es cuando una app tarda demasiado creando una estructura en memoria elaborada o calculando el siguiente movimiento en un juego en el subproceso de IU.
En Android, los servicios del sistema ActivityManager y WindowManager supervisan la capacidad de respuesta de las apps. Android muestra el diálogo de ANR de una app cuando detecta una de las siguientes condiciones:
- No se produce una respuesta a un evento de entrada (como presionar la pantalla o usar las teclas) después de 5 segundos.
- Un
BroadcastReceiverno termina de ejecutarse en un plazo de 10 a 20 segundos para intents en primer plano. Para obtener más información, consulta Tiempo de espera del receptor de emisión.
Cómo evitar los errores de ANR
Las siguientes son sugerencias generales para evitar los errores de ANR. Si quieres obtener más información para diagnosticar y depurar diferentes tipos de errores de ANR, consulta las otras páginas de esta sección.
Mantén el subproceso principal desbloqueado en todo momento y usa los subprocesos de forma estratégica.
No realices operaciones de bloqueo ni de larga duración en el subproceso principal de la app. En su lugar, usa corrutinas de Kotlin para transferir el trabajo a despachadores en segundo plano (como
Dispatchers.IOoDispatchers.Default). Usa mecanismos comoviewModelScopepara lanzar de forma segura estas tareas en segundo plano oLaunchedEffectpara activarlas en respuesta a los cambios de estado de Compose.Intenta minimizar cualquier contención de bloqueo entre el subproceso principal y otros subprocesos.
Minimiza cualquier trabajo no relacionado con la IU en el subproceso principal, como cuando se manejan transmisiones o se ejecutan servicios. Cualquier método o función que se ejecute en el subproceso de IU debe realizar el menor trabajo posible. En particular, las actividades deben hacer lo mínimo posible para configurarse en métodos clave de ciclo de vida, como
onCreateyonResume. Nunca realices operaciones de E/S ni cálculos pesados y de bloqueo directamente dentro de una función de componibilidad. Esto bloquea el subproceso de IU durante la composición y la recomposición. Consulta Descripción general de las tareas en segundo plano para obtener más información sobre las soluciones disponibles para programar el trabajo en un subproceso en segundo plano y comunicarse con la IU.Ten cuidado cuando compartas grupos de subprocesos entre componentes. No uses los mismos subprocesos para operaciones que podrían ser de bloqueo prolongado y tareas urgentes, como la recepción de transmisiones.
Procura que el inicio de la app sea rápido. Minimiza las operaciones lentas o de bloqueo en el código de inicio de la app, como los métodos que se ejecutan durante la configuración de la inyección de dependencias (como con Hilt) o los componentes inicializados con la biblioteca de inicio de apps de Jetpack. Puedes optimizar aún más el inicio de la app con Perfiles de Baseline, Perfiles de inicio y R8.
Si usas
BroadcastReceiver, considera ejecutar receptores de emisión en un subproceso que no sea el principal conContext.registerReceiver. Para obtener más información, consulta la sección Errores de ANR en BroadcastReceiver.- Si usas
goAsync, asegúrate de que se llame aPendingResult.finishrápidamente antes de que se agote el tiempo de espera del error de ANR.
- Si usas
Errores de ANR en BroadcastReceiver
El tiempo de ejecución de BroadcastReceiver está limitado porque los receptores de emisión están destinados a realizar cantidades pequeñas y discretas de trabajo en segundo plano, como guardar una opción de configuración o registrar una Notification. Al igual que con otros métodos llamados en el subproceso de IU, las apps deben evitar las operaciones o los cálculos de larga duración en un receptor de emisión. En lugar de realizar tareas de larga duración a través del subproceso de IU, ejecútalas en segundo plano para una ejecución posterior. Consulta Descripción general de las tareas en segundo plano para obtener más información sobre
posibles soluciones.
Otro problema común se presenta cuando los objetos BroadcastReceiver se ejecutan con demasiada frecuencia. La ejecución frecuente en segundo plano puede reducir la cantidad de memoria disponible para otras apps. Para obtener más información sobre cómo habilitar o inhabilitar objetos BroadcastReceiver de manera eficiente, consulta la Descripción general de las transmisiones.
Cómo reforzar la capacidad de respuesta
Por lo general, es de 100 a 200 ms el umbral más allá del cual los usuarios perciben la lentitud en una app. A continuación, se incluyen sugerencias adicionales para que tu app resulte responsiva:
Si tu app realiza trabajo en segundo plano en respuesta a la entrada del usuario, muestra que se está progresando, por ejemplo, con un
CircularProgressIndicatoroLinearProgressIndicatoren tu IU.En el caso de los juegos, realiza los cálculos de los movimientos en una corrutina en segundo plano o un subproceso de trabajo.
Si tu app tiene una fase de configuración inicial que consume mucho tiempo, considera mostrar una pantalla de presentación o renderizar tu composición inicial lo más rápido posible. Indica que la carga está en curso y completa el estado de la IU de forma asíncrona. En cualquier caso, te recomendamos que indiques de alguna manera que se está progresando, de modo que el usuario no perciba que la app está bloqueada.
Usa herramientas de rendimiento como Perfetto y el Generador de perfiles de CPU para determinar cuellos de botella en la capacidad de respuesta de tu app.