Lifecycle en Jetpack Compose   como parte de Android Jetpack.

Los componentes optimizados para ciclos de vida realizan acciones como respuesta a un cambio en el estado del ciclo de vida de la actividad host. El androidx.lifecycle.compose artefacto proporciona APIs dedicadas que limpian automáticamente los recursos cuando salen de la pantalla o cuando la aplicación pasa a segundo plano.

Entre las APIs clave, se incluyen las siguientes:

Estas integraciones proporcionan hooks convenientes para administrar ciclos de vida dentro de la jerarquía de Compose. En este documento, se describe cómo puedes usarlas en tu app.

Cómo recopilar el estado del ciclo de vida con flujos

Lifecycle expone una propiedad currentStateFlow que proporciona el Lifecycle.State actual como un StateFlow de Kotlin. Puedes recopilar este Flow como State. De esta forma, se permite que tu app lea los cambios del ciclo de vida durante la composición.

val lifecycleOwner = LocalLifecycleOwner.current
val stateFlow = lifecycleOwner.lifecycle.currentStateFlow

val currentLifecycleState by stateFlow.collectAsState()

Se puede acceder al ejemplo anterior con el módulo lifecycle-common. El método currentStateAsState() está disponible en el módulo lifecycle-runtime-compose, lo que te permite leer de manera conveniente el estado actual del ciclo de vida con una sola línea. En el siguiente ejemplo, se demuestra esto:

val lifecycleOwner = LocalLifecycleOwner.current
val currentLifecycleState = lifecycleOwner.lifecycle.currentStateAsState()

Cómo ejecutar código en eventos de ciclo de vida

En lugar de crear una clase independiente que implemente DefaultLifecycleObserver y agregarla manualmente al ciclo de vida, puedes declarar la lógica del ciclo de vida en línea con efectos específicos. LifecycleEffects te permite ejecutar un bloque cuando se produce un Lifecycle.Event en particular directamente dentro de la composición.

LifecycleEventEffect

Usa LifecycleEventEffect para ejecutar un bloque de código cuando se produce un evento específico. Esto es mejor para eventos únicos, como el registro o la analítica, en los que no se necesita éxito ni un resultado inmediato.

@Composable
fun AnalyticsTracker(screenName: String) {
    // Log an event when the app receives ON_RESUME (e.g. comes to foreground)
    LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
        Analytics.logView(screenName)
    }
}

LifecycleStartEffect

Para las operaciones de inicio/detención vinculadas que deben ejecutarse mientras se inicia la app (visible) y limpiarse cuando se detiene (en segundo plano), usa LifecycleStartEffect.

Al igual que otros efectos de Compose (como LaunchedEffect), LifecycleStartEffect acepta claves. Cuando la clave cambia, activa el bloque para que vuelva a ejecutarse.

Cuando se produce un evento Lifecycle.Event.ON_STOP o el efecto sale de la composición, ejecuta el bloque onStopOrDispose para limpiar cualquier trabajo que fuera parte del bloque de partida.

@Composable
fun LocationMonitor(locationManager: LocationManager) {
    // Starts monitoring when ON_START is dispatched
    // Stops monitoring when ON_STOP is dispatched
    //   (or the composable leaves the screen)
    LifecycleStartEffect(locationManager) {
        val listener = LocationListener { location ->
            /* update UI */
        }
        locationManager.requestLocationUpdates(listener)
        // The cleanup block automatically runs on ON_STOP or on disposal
        onStopOrDispose {
            locationManager.removeUpdates(listener)
        }
    }
}

Para obtener información sobre otros tipos de efectos secundarios, consulta Efectos secundarios en Compose.

LifecycleResumeEffect

LifecycleResumeEffect funciona de la misma manera que LifecycleStartEffect, pero está vinculado al evento Lifecycle.Event.ON_RESUME. También proporciona un bloque onPauseOrDispose que realiza la limpieza cuando se envía ON_PAUSE o el elemento componible sale de la pantalla.

Esta API es útil para los recursos que deben estar activos solo cuando el usuario interactúa con la app, por ejemplo, cámaras o animaciones.

@Composable
fun CameraPreview(cameraController: CameraController) {
    LifecycleResumeEffect(cameraController) {
        cameraController.startPreview()

        onPauseOrDispose {
            cameraController.stopPreview()
        }
    }
}

Accede a LifecycleOwner

En Compose, el LifecycleOwner está disponible de forma implícita a través de CompositionLocal llamado LocalLifecycleOwner. De forma predeterminada, el host raíz de tu jerarquía de composición proporciona este propietario.

val lifecycleOwner = LocalLifecycleOwner.current

Para muchas apps, es suficiente con inspeccionar este propietario predeterminado o pasarlo a efectos optimizados para ciclos de vida. Sin embargo, para la navegación personalizada o los diseños complejos, es posible que quieras crear tu propio LifecycleOwner para definir los estados del ciclo de vida en secciones específicas de la IU. Por ejemplo, las bibliotecas de navegación (como Navigation 3) lo hacen automáticamente para darle a cada pantalla individual su propio ciclo de vida.

Crea un LifecycleOwner personalizado

La API de rememberLifecycleOwner() te permite crear y recordar un personalizadoLifecycleOwner. Esto es especialmente útil para componentes como HorizontalPager, en los que solo quieres que la página visible y establecida sea RESUMED, mientras configuras un maxState de STARTED para las páginas adyacentes fuera de la pantalla.

val pagerState = rememberPagerState(pageCount = { 10 })

HorizontalPager(state = pagerState) { pageNum ->
    val pageLifecycleOwner = rememberLifecycleOwner(
        maxState = if (pagerState.settledPage == pageNum) {
            Lifecycle.State.RESUMED
        } else {
            Lifecycle.State.STARTED
        }
    )

    CompositionLocalProvider(LocalLifecycleOwner provides pageLifecycleOwner) {
        // Your pages here. Their lifecycle-aware components respect the
        // custom maxState defined above.
    }
}

Para obtener más información sobre CompositionLocal, consulta Datos de permisos local con CompositionLocal.

Prácticas recomendadas para componentes optimizados para ciclos de vida

  • Mantén tus controladores de IU tan simples como sea posible. No deberían intentar adquirir sus propios datos. En su lugar, usa un ViewModel a fin de que los cambios se reflejen en la IU.StateFlow
  • Intenta escribir IU basadas en datos, donde la responsabilidad del controlador de IU sea actualizar la IU a medida que los datos cambien o notificar las acciones del usuario al ViewModel.
  • Coloca la lógica de datos en la ViewModel clase. ViewModel funcionará como el conector entre el controlador de IU y el resto de la app. Sin embargo, ten en cuenta que recuperar datos (por ejemplo, de una red) no es responsabilidad de ViewModel. En cambio, ViewModel debería realizar una llamada al componente apropiado para recuperar los datos y, luego, proporcionar los resultados a l controlador de IU.
  • Usa corutinas de Kotlin para administrar tareas de larga duración y otras operaciones que se pueden ejecutar de manera asíncrona.
  • Mantén la lógica de inicio/detención dentro del elemento componible que realmente la necesita. De esta manera, la lógica se quita automáticamente si se quita ese elemento de la IU específico de la pantalla (por ejemplo, dentro de un gráfico de navegación o cuando la visibilidad es condicional).
  • Usa collectAsStateWithLifecycle para los datos. No inicies ni detengas manualmente la recopilación de Flow en función de los eventos del ciclo de vida. En su lugar, usa collectAsStateWithLifecycle para convertir flujos en estado de IU de manera eficiente. Esto ahorra batería y recursos porque los Flow se pausan cuando la app está en segundo plano.

Para obtener más información sobre Flow, consulta Otros tipos de estado compatibles.

Casos de uso de componentes optimizados para ciclos de vida

Los componentes optimizados para los ciclos de vida pueden facilitar la administración de los ciclos de vida en varios casos. Algunos ejemplos son los siguientes:

  • Alternar entre actualizaciones de ubicación aproximadas o detalladas. Usa LifecycleStartEffect para habilitar actualizaciones de ubicación detalladas mientras tu app esté visible (ON_START) y limpia automáticamente el objeto de escucha o cambia a actualizaciones aproximadas cuando la app esté en segundo plano (ON_STOP).
  • Iniciar y detener el almacenamiento de videos en búfer. Usa LifecycleResumeEffect para diferir la reproducción de video real hasta que la app esté completamente en primer plano y sea interactiva (ON_RESUME) y para asegurarte de que la reproducción se pause y libere recursos cuando la app esté en segundo plano (ON_PAUSE).
  • Iniciar y detener la transmisión de red. Usa collectAsStateWithLifecycle para observar flujos continuos de datos (como un flujo de Kotlin desde un socket de red). Esto te brinda actualizaciones en vivo mientras una app está en primer plano y cancela automáticamente la recopilación cuando la app pasa a segundo plano.
  • Pausar y reanudar tareas pesadas. Usa LifecycleResumeEffect para controlar la pausa de actualizaciones visuales pesadas cuando la app está en segundo plano y reanudarlas después de que la app esté en primer plano.

Cómo controlar eventos ON_STOP de forma segura

Compose está diseñado para controlar eventos ON_STOP de forma segura.

  • El estado es seguro: Puedes actualizar MutableState (por ejemplo, con uiState.value = ...) en cualquier momento, incluso cuando la app está en segundo plano. Compose espera hasta que la app esté visible para renderizar los cambios.
  • Limpieza automática: Con efectos como LifecycleStartEffect, tu bloque de limpieza (onStopOrDispose) se ejecuta exactamente cuando el ciclo de vida pasa a STOPPED. Esto evita que mantengas recursos pesados (como la cámara o la ubicación) mientras la app está en segundo plano.

Para obtener más información sobre MutableState, consulta El estado y Jetpack Compose.

Recursos adicionales

Puedes obtener más información sobre la administración de los ciclos de vida con componentes optimizados para ciclos de vida en los siguientes recursos adicionales.

Contenido de Views