Manual sobre devoluciones de llamadas y varios subprocesos

En el curso Desarrollo de apps para Android en Kotlin, se da por sentado que conoces el concepto y la terminología de los subprocesos múltiples. Esta página es una introducción de nivel superior a modo de repaso.

En la actualidad, la mayoría de los dispositivos tienen varios procesadores de hardware que se ejecutan de forma simultánea. Eso se conoce como procesamiento múltiple.

Para usar procesadores de forma más eficiente, el sistema operativo puede habilitar una aplicación a fin de crear más de un subproceso de ejecución dentro de un proceso. Eso se conoce como multiproceso.

imagen

Es como leer varios libros al mismo tiempo, alternar entre ellos después de cada capítulo y terminarlos todos, pero no puedes leer más de un libro al mismo tiempo.

Se necesita un poco de infraestructura para administrar todos esos subprocesos.

imagen

El programador tiene en cuenta aspectos como las prioridades y se asegura de que todos los subprocesos puedan ejecutarse y finalizar. Ningún libro debería estar en la estantería por mucho tiempo juntando polvo, pero si es muy largo o puede esperar, es posible que pase mucho antes de que decidas leerlo.

El despachador configura subprocesos; es decir, te envía los libros que debes leer y especifica un contexto para que eso suceda. Puedes considerar el contexto como una sala de lectura especializada, que es independiente. Algunos contextos son mejores para las operaciones de la interfaz de usuario, mientras que otros se especializan en operaciones de entrada y salida.

Lo único que debes saber es que una aplicación orientada al usuario, por lo general, tiene un subproceso principal que se ejecuta en primer plano y puede enviar otros subprocesos que se ejecuten en segundo plano.

En Android, el subproceso principal es un subproceso único que administra todas las actualizaciones de la IU. Este subproceso principal, conocido como subproceso de la IU, también es el subproceso que llama a todos los controladores de clics y a otras devoluciones de ciclo de vida e IU. El subproceso de IU es el predeterminado. A menos que tu app cambie de manera explícita los subprocesos o use una clase que se ejecute en un subproceso diferente, todo lo que hace tu app permanece en el subproceso principal.

Esto genera un desafío potencial. El subproceso de IU debe ejecutarse sin problemas para garantizar una excelente experiencia del usuario. Para que tu app se muestre al usuario sin pausas visibles, el subproceso principal debe actualizar la pantalla cada 16 ms o con más frecuencia, o aproximadamente a 60 fotogramas por segundo. A esta velocidad, los seres humanos perciben el cambio de fotogramas como completamente fluido. Son muchos fotogramas y poco tiempo. Por lo tanto, en Android, es fundamental evitar el bloqueo del subproceso de IU. El bloqueo en este contexto significa que el subproceso de IU no realiza ninguna acción mientras espera que finalice la actualización de la base de datos.

imagen

Muchas tareas comunes tardan más de 16 milisegundos, como obtener datos de Internet, leer un archivo grande o escribir datos en una base de datos. Por lo tanto, llamar al código para realizar tareas como las del subproceso principal puede hacer que la app se detenga, salte o incluso se bloquee. Si bloqueas el subproceso principal durante demasiado tiempo, la app incluso puede fallar y mostrar un diálogo de "Aplicación no responde" (ANR).

Devoluciones de llamada

Hay varias opciones para trabajar desde el subproceso principal.

Un patrón para realizar tareas de larga duración sin bloquear el subproceso principal es la devolución de llamadas. Si usas devoluciones de llamada, podrás iniciar tareas de larga duración en un subproceso que se ejecute en segundo plano. Cuando se completa la tarea, se llama a la devolución de llamada, como un argumento, para informar a tu código el resultado del subproceso principal.

Las devoluciones de llamada son un patrón muy útil, pero tienen algunos inconvenientes. El código que utiliza devoluciones de llamadas en gran medida puede ser difícil de leer y razonar. Como el código parece secuencial, el código de la devolución de llamada se ejecutará en algún momento asíncrono del futuro. Además, las devoluciones de llamada no permiten el uso de algunas funciones del lenguaje, como excepciones.

Corrutinas

En Kotlin, las corrutinas son la solución para procesar tareas de larga duración de forma elegante y eficiente. Las corrutinas de Kotlin te permiten convertir en código secuencial el código basado en devoluciones de llamada. Por lo general, el código escrito de forma secuencial es más fácil de leer y hasta puede usar funciones de lenguaje como excepciones. En definitiva, las corrutinas y las devoluciones de llamada hacen lo mismo: esperan hasta que un resultado esté disponible desde una tarea de larga duración y continúan su ejecución.