Las corrutinas de Kotlin proporcionan una API que te permite escribir código asíncrono. Con las corrutinas de Kotlin, puedes definir un CoroutineScope, lo que te ayuda a administrar cuándo deben ejecutarse las corrutinas. Cada operación asíncrona se ejecuta dentro de un alcance particular.
Los componentes optimizados para ciclos de vida proporcionan compatibilidad de primer nivel con las corrutinas para alcances lógicos en tu app. En este documento, se explica cómo usar corrutinas de manera eficaz con componentes optimizados para ciclos de vida.
Cómo agregar dependencias
Los alcances integrados de las corrutinas que se describen en este tema se encuentran en la API de Lifecycle. Asegúrate de agregar las dependencias apropiadas cuando uses estos alcances.
- Para las utilidades de ViewModel en Compose, usa
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"). - Para las utilidades de Lifecycle en Compose, usa
implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version").
Ámbitos de corrutinas optimizados para ciclos de vida
Compose y las bibliotecas de Lifecycle proporcionan los siguientes alcances integrados que puedes usar en tu app.
ViewModelScope
Se define un ViewModelScope para cada ViewModel en tu app. Si se borra ViewModel, se cancela automáticamente cualquier corrutina iniciada en este alcance. Las corrutinas son útiles para cuando tienes trabajos que se deben hacer solo si ViewModel está activo. Por ejemplo, si estás procesando datos para un diseño, debes definir el alcance del trabajo en el ViewModel, de modo que, si se borra el ViewModel, se cancele automáticamente el trabajo para no consumir recursos.
Puedes acceder al CoroutineScope de un ViewModel a través de la propiedad viewModelScope del ViewModel, como se muestra en el siguiente ejemplo:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
Para casos de uso más avanzados, puedes pasar un CoroutineScope personalizado directamente al constructor del ViewModel para reemplazar el viewModelScope predeterminado. Este enfoque ofrece más control y flexibilidad, en especial para lo siguiente:
Pruebas: Te permite insertar un
TestScope, lo que facilita el control del tiempo y la verificación del comportamiento de las corrutinas en las pruebas de unidades.Configuración personalizada: Puedes configurar el alcance con un
CoroutineDispatcherespecífico (comoDispatchers.Defaultpara un procesamiento pesado) o unCoroutineExceptionHandlerpersonalizado antes de que el ViewModel comience su trabajo.
Permisos vinculados a la composición
Los efectos secundarios, como las animaciones, las llamadas de red o los temporizadores, deben limitarse al ciclo de vida del elemento componible. De esta manera, cuando un elemento componible sale de la pantalla (sale de la composición), se cancelan automáticamente todas las corrutinas en ejecución para evitar pérdidas de memoria.
Compose proporciona la API de LaunchedEffect para controlar el alcance de la composición de forma declarativa.
LaunchedEffect crea un CoroutineScope que te permite ejecutar funciones de suspensión. El alcance está vinculado al ciclo de vida de la composición del elemento componible, no al Lifecycle de la actividad host.
- Enter: La corrutina comienza cuando el elemento componible ingresa a la composición.
- Salida: La corrutina se cancela cuando el elemento componible sale de la composición.
- Relanzamiento: Si cambia alguna clave que se pasó a
LaunchedEffect, se cancela la corrutina existente y se lanza una nueva.
En el siguiente ejemplo, se muestra cómo usar LaunchedEffect para crear una animación de pulsación. La corrutina está vinculada a la presencia del elemento componible en la composición y reacciona a los cambios de configuración:
// Allow the pulse rate to be configured, so it can be sped up if the user is running // out of time var pulseRateMs by remember { mutableLongStateOf(3000L) } val alpha = remember { Animatable(1f) } LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes while (isActive) { delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user alpha.animateTo(0f) alpha.animateTo(1f) } }
Para obtener más información sobre LaunchedEffect, consulta Efectos secundarios en Compose.
Recopilación de flujos optimizados para ciclos de vida
Para recopilar flujos de forma segura en Jetpack Compose, usa la API de collectAsStateWithLifecycle. Esta única función convierte un Flow en un objeto State de Compose y administra automáticamente la suscripción del ciclo de vida por ti. De forma predeterminada, la recopilación comienza cuando el ciclo de vida es STARTED y se detiene cuando el ciclo de vida es STOPPED. Para anular este comportamiento predeterminado, pasa el parámetro minActiveState con el método de ciclo de vida que desees, como Lifecycle.State.RESUMED.
En el siguiente ejemplo, se muestra cómo recopilar el StateFlow de un ViewModel en un elemento componible:
@Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val messages by conversationViewModel.messages.collectAsStateWithLifecycle() ConversationScreen( messages = messages, onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) } ) } @Composable private fun ConversationScreen( messages: List<Message>, onSendMessage: (Message) -> Unit ) { MessagesList(messages, onSendMessage) /* ... */ }
Recolección paralela de varios flujos
En Compose, puedes recopilar varios flujos en paralelo declarando múltiples variables de estado. Dado que collectAsStateWithLifecycle administra su propio alcance subyacente, la recopilación paralela se controla automáticamente:
@Composable
fun DashboardScreen(viewModel: DashboardViewModel = viewModel()) {
// Both flows are collected safely in parallel and will emit updates when either changes, the composables will recompose
val userData by viewModel.userFlow.collectAsStateWithLifecycle()
val feedData by viewModel.feedFlow.collectAsStateWithLifecycle()
// ...
}
Cómo calcular valores de forma asíncrona con Flows
Cuando necesites calcular valores de forma asíncrona, usa StateFlow con el operador stateIn.
En el siguiente fragmento, se usa un Flow estándar convertido en un StateFlow. El parámetro WhileSubscribed(5000) mantiene la suscripción activa durante cinco segundos después de que desaparece la IU para controlar los cambios de configuración.
val uiState: StateFlow<Result> = flow {
emit(repository.fetchData())
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Result.Loading
)
Usa collectAsStateWithLifecycle para convertir los valores recopilados en State de Compose, de modo que tu IU pueda actualizarse de forma reactiva cada vez que cambien los datos.
Para obtener más información sobre el estado, consulta El estado y Jetpack Compose.
Recursos adicionales
Mira contenido
Ejemplos
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo manejar ciclos de vida con componentes optimizados para ciclos de vida
- Cómo cargar y mostrar datos paginados