Introducción a las corrutinas en Android Studio

1. Antes de comenzar

En el codelab anterior, aprendiste sobre las corrutinas. Usaste el Playground de Kotlin para escribir código simultáneo con corrutinas. En este codelab, aplicarás tu conocimiento sobre corrutinas dentro de una app para Android y su ciclo de vida. Agregarás código para iniciar corrutinas nuevas de forma simultánea y aprenderás a probarlas.

Requisitos previos

  • Tener conocimientos de los conceptos básicos del lenguaje Kotlin, incluidas funciones y lambdas
  • Poder compilar diseños en Jetpack Compose
  • Poder escribir pruebas de unidades en Kotlin (consulta el codelab Cómo escribir pruebas de unidades para ViewModel)
  • Conocer el funcionamiento de los subprocesos y la simultaneidad
  • Contar con conocimientos básicos de corrutinas y CoroutineScope

Qué compilarás

  • Compilarás la app de Race Tracker, que simula el progreso de una carrera entre dos jugadores. Considera esta app como una oportunidad para experimentar y aprender acerca de los diferentes aspectos de las corrutinas.

Qué aprenderás

  • Cómo usar corrutinas en el ciclo de vida de apps para Android
  • Cuáles son los principios de simultaneidad estructurada
  • Cómo escribir pruebas de unidades para probar las corrutinas

Requisitos

  • La versión estable más reciente de Android Studio

2. Descripción general de la app

La app de Race Tracker simula dos jugadores que corren una carrera. La IU de la app consta de dos botones, Start/Pause y Reset, y dos barras de progreso que muestran el progreso de los corredores. Los jugadores 1 y 2 están configurados para "correr" la carrera a diferentes velocidades. Cuando comienza la carrera, el Jugador 2 avanza dos veces más rápido que el Jugador 1.

Usarás las corrutinas en la app para asegurarte de lo siguiente:

  • Ambos jugadores "corren la carrera" al mismo tiempo.
  • La IU de la app es responsiva y las barras de progreso aumentan durante la carrera.

El código de partida tiene el código de IU listo para la app de Race Tracker. El objetivo principal de esta parte del codelab es que te familiarices con las corrutinas de Kotlin dentro de una app para Android.

Obtén el código de partida

Para comenzar, descarga el código de partida:

Descargar ZIP

Como alternativa, puedes clonar el repositorio de GitHub para el código:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-race-tracker.git
$ cd basic-android-kotlin-compose-training-race-tracker
$ git checkout starter

Puedes explorar el código de partida en el repositorio de GitHub de Race Tracker.

Explicación del código de inicio

Para iniciar la carrera, haz clic en el botón Start. El texto del botón Start cambia a Pause mientras la carrera está en curso.

2ee492f277625f0a.png

En cualquier momento, puedes usar este botón para detener o continuar la carrera.

50e992f4cf6836b7.png

Cuando comience la carrera, podrás ver el progreso de cada jugador a través de un indicador de estado. La función StatusIndicator de componibilidad muestra el estado de progreso de cada jugador. Usa el elemento componible LinearProgressIndicator para mostrar la barra de progreso. Usarás corrutinas para actualizar el valor del progreso.

79cf74d82eacae6f.png

RaceParticipant proporciona los datos para el incremento de progreso. Esta clase es un contenedor de estado para cada uno de los jugadores y mantiene el name del participante, el maxProgress que se debe alcanzar para finalizar la carrera, la demora entre los incrementos de progreso, el currentProgress en la carrera y el initialProgress.

En la siguiente sección, usarás corrutinas a los efectos de implementar la funcionalidad que simula el progreso de la carrera sin bloquear la IU de la app.

3. Cómo implementar el progreso de la carrera

Necesitas la función run(), que compara el currentProgress del jugador con el maxProgress, que refleja el progreso total de la carrera, y usa la función de suspensión delay() para agregar un ligero retraso entre los incrementos de progreso. Esta debe ser una función suspend, ya que llama a otra función de suspensión delay(). Además, llamarás a esta función más adelante en el codelab desde una corrutina. Sigue estos pasos para implementar la función:

  1. Abre la clase RaceParticipant, que es parte del código de partida.
  2. Dentro de la clase RaceParticipant, define una nueva función suspend llamada run().
class RaceParticipant(
    ...
) {
    var currentProgress by mutableStateOf(initialProgress)
        private set

    suspend fun run() {

    }
    ...
}
  1. Para simular el progreso de la carrera, agrega un bucle while que se ejecute hasta que currentProgress alcance el valor de maxProgress, que se establece en 100.
class RaceParticipant(
    ...
    val maxProgress: Int = 100,
    ...
) {
    var currentProgress by mutableStateOf(initialProgress)
        private set

    suspend fun run() {
        while (currentProgress < maxProgress) {

        }
    }
    ...
}
  1. El valor de currentProgress se establece en initialProgress, que es 0. Para simular el progreso del participante, aumenta el valor de currentProgress en el valor de la propiedad progressIncrement dentro del bucle while. Ten en cuenta que el valor predeterminado de progressIncrement es 1.
class RaceParticipant(
    ...
    val maxProgress: Int = 100,
    ...
    private val progressIncrement: Int = 1,
    private val initialProgress: Int = 0
) {
    ...
    var currentProgress by mutableStateOf(initialProgress)
        private set

    suspend fun run() {
        while (currentProgress < maxProgress) {
            currentProgress += progressIncrement
        }
    }
}
  1. Si deseas simular diferentes intervalos de progreso en la carrera, usa la función de suspensión delay(). Pasa el valor de la propiedad progressDelayMillis como argumento.
suspend fun run() {
    while (currentProgress < maxProgress) {
        delay(progressDelayMillis)
        currentProgress += progressIncrement
    }
}

Si observas el código que acabas de agregar, verás un ícono a la izquierda de la llamada para la función delay() en Android Studio, como se muestra en la siguiente captura de pantalla: 11b5df57dcb744dc.png

Este ícono indica el punto de suspensión en el que la función podría suspenderse y reanudarse más tarde.

El subproceso principal no se bloquea mientras la corrutina está esperando para completar la duración del retraso, como se muestra en el siguiente diagrama:

a3c314fb082a9626.png

La corrutina suspende (pero no bloquea) la ejecución después de llamar a la función delay() con el valor de intervalo deseado. Una vez que se completa el retraso, la corrutina reanuda la ejecución y actualiza el valor de la propiedad currentProgress.

4. Cómo iniciar la carrera

Cuando el usuario presiona el botón Start, debes "iniciar la carrera" mediante una llamada a la función de suspensión run() en cada una de las dos instancias de los jugadores. Para ello, debes iniciar una corrutina a fin de llamar a la función run().

Cuando inicias una corrutina a fin de activar la carrera, debes garantizar los siguientes aspectos para ambos participantes:

  • Ambos comienzan a correr en cuanto se hace clic en el botón Start, es decir, cuando se inician las corrutinas.
  • Pausan o detienen su carrera cuando se hace clic en el botón Pause o Reset, respectivamente, es decir, cuando se cancelan las corrutinas.
  • Cuando el usuario cierra la app, la cancelación se administra correctamente, es decir, todas las corrutinas se cancelan y se vinculan a un ciclo de vida.

En el primer codelab, aprendiste que solo se puede llamar a una función de suspensión desde otra. Para llamar a funciones de suspensión de forma segura dentro de un elemento componible, debes usar el elemento LaunchedEffect() componible. El elemento componible LaunchedEffect() ejecuta la función de suspensión proporcionada durante el tiempo que permanezca en la composición. Puedes usar la función de componibilidad LaunchedEffect() para lograr todo lo siguiente:

  • El elemento componible LaunchedEffect() te permite llamar de forma segura a las funciones de suspensión desde los elementos componibles.
  • Cuando la función LaunchedEffect() ingresa a la composición, inicia una corrutina con el bloque de código pasado como un parámetro. Ejecuta la función de suspensión proporcionada mientras permanezca en la composición. Cuando un usuario hace clic en el botón Start en la app de RaceTracker, la función LaunchedEffect() ingresa a la composición e inicia una corrutina para actualizar el progreso.
  • La corrutina se cancela cuando LaunchedEffect() sale de la composición. En la app, si el usuario hace clic en el botón Reset/Pause, se quita LaunchedEffect() de la composición y se cancelan las corrutinas subyacentes.

En la app de RaceTracker, no tienes que proporcionar un componente Dispatcher de forma explícita, ya que LaunchedEffect() se encarga de ello.

Para iniciar la carrera, llama a la función run() para cada participante y realiza los siguientes pasos:

  1. Abre el archivo RaceTrackerApp.kt ubicado en el paquete com.example.racetracker.ui.
  2. Navega al elemento componible RaceTrackerApp() y agrega una llamada al elemento componible LaunchedEffect() en la línea siguiente a la definición de raceInProgress.
@Composable
fun RaceTrackerApp() {
    ...
    var raceInProgress by remember { mutableStateOf(false) }

    LaunchedEffect {

    }
    RaceTrackerScreen(...)
}
  1. Para asegurarte de que, si las instancias de playerOne o playerTwo se reemplazan por instancias diferentes, LaunchedEffect() debe cancelar y reiniciar las corrutinas subyacentes, agrega los objetos playerOne y playerTwo como key del LaunchedEffect. Así como se recompone un elemento componible Text() cuando cambia su valor de texto, si cambia alguno de los argumentos clave de LaunchedEffect(), la corrutina subyacente se cancela y se reinicia.
LaunchedEffect(playerOne, playerTwo) {
}
  1. Agrega una llamada a las funciones playerOne.run() y playerTwo.run().
@Composable
fun RaceTrackerApp() {
    ...
    var raceInProgress by remember { mutableStateOf(false) }

    LaunchedEffect(playerOne, playerTwo) {
        playerOne.run()
        playerTwo.run()
    }
    RaceTrackerScreen(...)
}
  1. Une el bloque de LaunchedEffect() con una condición if. El valor inicial para este estado es false. El valor del estado de raceInProgress se actualiza a true cuando el usuario hace clic en el botón Start y se ejecuta LaunchedEffect().
if (raceInProgress) {
    LaunchedEffect(playerOne, playerTwo) {
        playerOne.run()
        playerTwo.run()
    }
}
  1. Actualiza la marca raceInProgress a false para finalizar la carrera. Este valor también se establece en false cuando el usuario hace clic en Pause. Cuando este valor se establece en false, la función LaunchedEffect() garantiza que se cancelen todas las corrutinas iniciadas.
LaunchedEffect(playerOne, playerTwo) {
    playerOne.run()
    playerTwo.run()
    raceInProgress = false
}
  1. Ejecuta la app y haz clic en Start. Deberías ver que el jugador uno completa la carrera antes de que el jugador dos comience a correr, como se muestra en el siguiente video:

fa0630395ee18f21.gif

Esto no parece ser una carrera justa. En la siguiente sección, aprenderás a iniciar tareas simultáneas de modo que ambos jugadores puedan correr al mismo tiempo. También comprenderás los conceptos e implementarás este comportamiento.

5. Simultaneidad estructurada

La forma en que escribes código mediante corrutinas se denomina simultaneidad estructurada. Este estilo de programación mejora la legibilidad y el tiempo de desarrollo de tu código. La idea de la simultaneidad estructurada es que las corrutinas tienen una jerarquía: las tareas pueden iniciar subtareas, que a su vez pueden iniciar otras subtareas. A la unidad de esta jerarquía se la conoce como el alcance de la corrutina. Los alcances de las corrutinas siempre deben estar asociados con un ciclo de vida.

Las APIs de corrutinas se ajustan a esta simultaneidad estructurada de forma predeterminada. No puedes llamar a una función de suspensión desde una función que no está marcada como tal. Esta limitación garantiza que llames a las funciones de suspensión desde los compiladores de corrutinas, como launch. Estos compiladores están, a su vez, vinculados a un CoroutineScope.

6. Cómo iniciar tareas simultáneas

  1. Para permitir que ambos participantes corran en simultáneo, debes iniciar dos corrutinas separadas y mover cada llamada a la función run() dentro de esas corrutinas. Une la llamada a la función playerOne.run() con el compilador de launch.
LaunchedEffect(playerOne, playerTwo) {
    launch { playerOne.run() }
    playerTwo.run()
    raceInProgress = false
}
  1. De manera similar, une la llamada a la función playerTwo.run() con el compilador de launch. Con este cambio, la app inicia dos corrutinas que se ejecutan simultáneamente. Ahora ambos jugadores pueden correr al mismo tiempo.
LaunchedEffect(playerOne, playerTwo) {
    launch { playerOne.run() }
    launch { playerTwo.run() }
    raceInProgress = false
}
  1. Ejecuta la app y haz clic en Start. Mientras esperas que comience la carrera, el texto del botón cambia inmediatamente a Start de forma inesperada.

c46c2aa7c580b27b.png

Cuando ambos jugadores completan la carrera, la app de Race Tracker debe restablecer el texto del botón Pause a Start. Sin embargo, en este momento, la app actualiza el raceInProgress justo después de iniciar las corrutinas sin esperar a que los jugadores completen la carrera:

LaunchedEffect(playerOne, playerTwo) {
    launch {playerOne.run() }
    launch {playerTwo.run() }
    raceInProgress = false // This will update the state immediately, without waiting for players to finish run() execution.
}

La marca raceInProgress se actualiza de inmediato por los siguientes motivos:

  • La función del compilador de launch inicia una corrutina para ejecutar la función playerOne.run() y, acto seguido, ejecuta la siguiente línea en el bloque de código.
  • Se ejecuta el mismo flujo con la segunda función del compilador de launch que ejecuta la función playerTwo.run().
  • En cuanto se muestra el segundo compilador de launch, se actualiza la marca raceInProgress. Esto cambia inmediatamente el texto del botón a Start y la carrera no comienza.

Alcance de las corrutinas

La función de suspensión coroutineScope crea un CoroutineScope y llama al bloque de suspensión especificado con el alcance actual. El alcance hereda su coroutineContext del alcance de la función LaunchedEffect().

El alcance se muestra en cuanto se completan el bloque especificado y todas las corrutinas secundarias. Para la app de RaceTracker, se muestra una vez que ambos objetos participantes finalizan la ejecución de la función run().

  1. Para garantizar que la función run() de playerOne y playerTwo complete su ejecución antes de actualizar la marca raceInProgress, une ambos compiladores con un bloque de coroutineScope.
LaunchedEffect(playerOne, playerTwo) {
    coroutineScope {
        launch { playerOne.run() }
        launch { playerTwo.run() }
    }
    raceInProgress = false
}
  1. Ejecuta la app en un emulador o dispositivo Android. Deberías ver la siguiente pantalla:

598ee57f8ba58a52.png

  1. Haz clic en el botón Start. El Jugador 2 corre más rápido que el Jugador 1. Cuando se completa la carrera (es decir, cuando ambos jugadores alcanzan el 100% de progreso), la etiqueta del botón Pause cambia a Start. Puedes hacer clic en el botón Reset para restablecer la carrera y volver a ejecutar la simulación. La carrera se muestra en el siguiente video.

c1035eecc5513c58.gif

El flujo de ejecución se muestra en el siguiente diagrama:

cf724160fd66ff21.png

  • Cuando se ejecuta el bloque de la función LaunchedEffect(), el control se transfiere al bloque de coroutineScope{..}.
  • El bloque de coroutineScope inicia ambas corrutinas de forma simultánea y espera a que terminen de ejecutarse.
  • Una vez que se completa la ejecución, se actualiza la marca de raceInProgress.

El bloque de coroutineScope solo se muestra y avanza después de que todo el código dentro del bloque completa la ejecución. Para el código fuera del bloque, la presencia o ausencia de simultaneidad se convierte en un mero detalle de implementación. Este estilo de programación proporciona un enfoque estructurado de programación simultánea y se denomina simultaneidad estructurada.

Cuando haces clic en el botón Reset luego de que finaliza la carrera, las corrutinas se cancelan y el progreso de ambos jugadores se restablece a 0.

Si deseas ver cómo se cancelan las corrutinas cuando el usuario hace clic en el botón Reset, sigue estos pasos:

  1. Une el cuerpo del método run() en un bloque try-catch como se muestra en el siguiente código:
suspend fun run() {
    try {
        while (currentProgress < maxProgress) {
            delay(progressDelayMillis)
            currentProgress += progressIncrement
        }
    } catch (e: CancellationException) {
        Log.e("RaceParticipant", "$name: ${e.message}")
        throw e // Always re-throw CancellationException.
    }
}
  1. Ejecuta la app y haz clic en el botón Start.
  2. Después de algunos incrementos de progreso, haz clic en el botón Reset.
  3. Asegúrate de ver el siguiente mensaje impreso en Logcat:
Player 1: StandaloneCoroutine was cancelled
Player 2: StandaloneCoroutine was cancelled

7. Cómo escribir pruebas de unidades para probar corrutinas

El código de prueba de unidades que usa corrutinas requiere atención adicional, ya que su ejecución puede ser asíncrona y ocurrir en varios subprocesos.

Para llamar a funciones de suspensión en las pruebas, debes estar en una corrutina. Como las funciones de prueba JUnit no son funciones de suspensión, debes usar el compilador de corrutinas runTest. Este compilador forma parte de la biblioteca kotlinx-coroutines-test y está diseñado para ejecutar pruebas. El compilador ejecuta el cuerpo de la prueba en una corrutina nueva.

Como runTest es parte de la biblioteca kotlinx-coroutines-test, debes agregar su dependencia.

Para agregar la dependencia, completa los siguientes pasos:

  1. Abre el archivo build.gradle.kts del módulo de la app, ubicado en el directorio app del panel Project.

785ba583eaea1275.png

  1. Dentro del archivo, desplázate hacia abajo hasta encontrar el bloque dependencies{}.
  2. Agrega una dependencia con la configuración de testImplementation a la biblioteca kotlinx-coroutines-test.
plugins {
    ...
}

android {
    ...
}

dependencies {
    ...
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
}
  1. En la barra de notificaciones que se encuentra en la parte superior del archivo build.gradle.kts, haz clic en Sync Now para permitir que la importación y la compilación finalicen, como se muestra en la siguiente captura de pantalla:

1c20fc10750ca60c.png

Una vez completada la compilación, puedes comenzar a escribir pruebas.

Cómo implementar pruebas de unidades para iniciar y finalizar la carrera

A fin de garantizar que el progreso de la carrera se actualice correctamente durante las diferentes fases, las pruebas de unidades deben abarcar diferentes situaciones. En este codelab, se abarcan dos de ellas:

  • El progreso después de que comienza la carrera
  • El progreso después de que termina la carrera

Para verificar si el progreso de la carrera se actualiza correctamente después de que comienza, debes confirmar que el progreso actual se establece en 1 luego de pasar la duración de raceParticipant.progressDelayMillis.

A fin de implementar la situación de prueba, sigue estos pasos:

  1. Navega al archivo RaceParticipantTest.kt ubicado debajo del conjunto de orígenes de prueba.
  2. Para definir la prueba, después de la definición de raceParticipant, crea una función raceParticipant_RaceStarted_ProgressUpdated() y anótala con la anotación @Test. Dado que el bloque de prueba debe colocarse en el compilador de runTest, usa la sintaxis de expresión para mostrar el bloque runTest() como resultado de la prueba.
class RaceParticipantTest {
    private val raceParticipant = RaceParticipant(
        ...
    )

    @Test
    fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    }
}
  1. Agrega una variable expectedProgress de solo lectura y establécela en 1.
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    val expectedProgress = 1
}
  1. Para simular el inicio de la carrera, usa el compilador de launch para iniciar una corrutina nueva y llamar a la función raceParticipant.run().
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    val expectedProgress = 1
    launch { raceParticipant.run() }
}

El valor de la propiedad raceParticipant.progressDelayMillis determina la duración después de la cual se actualiza el progreso de la carrera. A fin de probar el progreso una vez transcurrido el tiempo de progressDelayMillis, debes agregar algún tipo de retraso a la prueba.

  1. Usa la función auxiliar advanceTimeBy() para aumentar el tiempo según el valor de raceParticipant.progressDelayMillis. La función advanceTimeBy() ayuda a reducir el tiempo de ejecución de la prueba.
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    val expectedProgress = 1
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.progressDelayMillis)
}
  1. Dado que advanceTimeBy() no ejecuta la tarea programada en la duración determinada, debes llamar a la función runCurrent(). Esta función ejecuta las tareas pendientes del momento actual.
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    val expectedProgress = 1
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.progressDelayMillis)
    runCurrent()
}
  1. Para garantizar que se actualice el progreso, agrega una llamada a la función assertEquals() a fin de verificar si el valor de la propiedad raceParticipant.currentProgress coincide con el de la variable expectedProgress.
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
    val expectedProgress = 1
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.progressDelayMillis)
    runCurrent()
    assertEquals(expectedProgress, raceParticipant.currentProgress)
}
  1. Ejecuta la prueba para confirmar que se complete con éxito.

Para verificar si el progreso de la carrera se actualiza correctamente después de que termina, debes confirmar que el progreso actual se establece en 100 al finalizar la carrera.

Sigue estos pasos para implementar la prueba:

  1. Después de la función de prueba raceParticipant_RaceStarted_ProgressUpdated(), crea una función raceParticipant_RaceFinished_ProgressUpdated() y anótala con la anotación @Test. La función debe mostrar un resultado de prueba del bloque runTest{}.
class RaceParticipantTest {
    ...

    @Test
    fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
        ...
    }

    @Test
    fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
    }
}
  1. Usa el compilador de launch para iniciar una corrutina nueva y agregar una llamada a la función raceParticipant.run().
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
    launch { raceParticipant.run() }
}
  1. Con el fin de simular el final de la carrera, usa la función advanceTimeBy() para adelantar el tiempo del despachador en raceParticipant.maxProgress * raceParticipant.progressDelayMillis:
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
}
  1. Agrega una llamada a la función runCurrent() a los efectos de ejecutar las tareas pendientes.
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
    runCurrent()
}
  1. Para garantizar que el progreso se actualice correctamente, agrega una llamada a la función assertEquals() a fin de verificar si el valor de la propiedad raceParticipant.currentProgress es igual a 100.
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
    launch { raceParticipant.run() }
    advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
    runCurrent()
    assertEquals(100, raceParticipant.currentProgress)
}
  1. Ejecuta la prueba para confirmar que se complete con éxito.

Prueba este desafío

Aplica las estrategias de prueba que se analizaron en el codelab Cómo escribir pruebas de unidades para ViewModel. Agrega las pruebas para cubrir el camino feliz, los casos de error y los casos límite.

Compara la prueba que escribes con las que están disponibles en el código de la solución.

8. Obtén el código de la solución

Para descargar el código del codelab terminado, puedes usar estos comandos de git:

git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-race-tracker.git
cd basic-android-kotlin-compose-training-race-tracker

También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.

Descargar ZIP

Si deseas ver el código de la solución, puedes hacerlo en GitHub.

9. Conclusión

¡Felicitaciones! Aprendiste a usar corrutinas para controlar la simultaneidad. Las corrutinas ayudan a administrar tareas de larga duración que, de lo contrario, podrían bloquear el subproceso principal y hacer que tu app deje de responder. También aprendiste a escribir pruebas de unidades para probar las corrutinas.

Las siguientes funciones son algunos de los beneficios de usar corrutinas:

  • Legibilidad: El código que escribes con corrutinas proporciona una comprensión clara de la secuencia que ejecuta las líneas de código.
  • Integración con Jetpack: Muchas bibliotecas de Jetpack, como Compose y ViewModel, incluyen extensiones que brindan compatibilidad completa con corrutinas. Además, algunas bibliotecas proporcionan su propio alcance de corrutina, que puedes usar para la simultaneidad estructurada.
  • Simultaneidad estructurada: Las corrutinas hacen que el código simultáneo sea seguro y fácil de implementar, eliminan el código estándar innecesario y garantizan que no se pierdan las corrutinas iniciadas por la app ni que sigan gastando recursos.

Resumen

  • Las corrutinas te permiten escribir código de larga duración que se ejecuta de forma simultánea sin aprender un nuevo estilo de programación. La ejecución de una corrutina es secuencial por diseño.
  • La palabra clave suspend se usa para marcar una función, o un tipo de función, a fin de indicar su disponibilidad para ejecutar, detener y reanudar un conjunto de instrucciones de código.
  • Una función suspend solo se puede llamar desde otra función de suspensión.
  • Puedes iniciar una corrutina nueva con la función de compilador launch o async.
  • El contexto de las corrutinas, los compiladores de corrutinas, Job, el alcance de las corrutinas y Dispatcher son los componentes principales para implementar corrutinas.
  • Las corrutinas usan despachadores a fin de determinar los subprocesos que se usarán en su ejecución.
  • Job desempeña un papel importante para garantizar la simultaneidad estructurada, ya que administra el ciclo de vida de las corrutinas y mantiene la relación de superior y secundario.
  • Un CoroutineContext define el comportamiento de una corrutina a través de Job y un despachador de corrutinas.
  • Un CoroutineScope controla la vida útil de las corrutinas a través de Job y aplica de manera forzosa la cancelación y otras reglas a sus trabajos secundarios y a los secundarios de estos de manera recurrente.
  • El inicio, la finalización, la cancelación y el error son cuatro operaciones frecuentes en la ejecución de la corrutina.
  • Las corrutinas siguen un principio de simultaneidad estructurada.

Más información