Hacer collect a un StateFlow en Android de forma segura en Jetpack Compose.

Cuando usamos StateFlows en Android podemos tomar los datos emitidos por ese StateFlow con collectState desde nuestro View en Jetpack Compose, de esta manera podemos usar el estado para pintar lo que queramos en la pantalla:

@Composable
fun MyComposableScreen(
myViewModel: MyViewModel = hiltViewModel(),
) {

  val myState = myViewModel.myStateFlow.collectAsState()

  // Aquí podemos usar myState como nos de la gana, por ejemplosupongamos que myViewModel descarga un usuario cuando se inicia y luego guarda ese usuario en myState, pues podríamos pintar el correo del usuario
  Text(
   text = myState.value.user.email
  )
}

Por lo general esto no es un problema porque en estos días nuestro internet es rápido, pero supongamos que sucede una o ambas de estas cosas:

  • Un usuario tiene internet super lento.
  • En lugar de descargar un usuario, descargamos un montón de datos, por ejemplo una lista de datos.

Ahora supongamos que este usuario abre la pantalla MyComposableScreen, pero como su internet es lento y son muchos datos se desespera y cierra la pantalla o la app completa, collectAsState() no tiene un mecanismo para saber el estado del ciclo de vida de nuestra Activity y por lo tanto pueden surgir problemas, comúnmente uno de estos dos:

  • Un memory leak, esto significa que cuando los datos terminen de cargarse collectAsState() los va a tomar y quedarán en memoria, si el usuario entra y sale muchas veces de la pantalla la app podría tomar mucha memoria del dispositivo.
  • Que cuando se finalmente los datos se descarguen se haga collect del estado y trate de pintarlo, pero como la Activity ya no existe la app truene (crashee) al no haber un contexto.

Para solucionar esto en lugar de collectAsState() deberíamos usar collectAsStateWithLifecycle(), su función es la misma pero el segundo está al tanto del estado en el que se encuentra el ciclo de vida de nuestra Activity, así, empieza a intentar hacer collect en Activity onStart() y termina de hacerlo en onStop()

Para usar collectAsStateWithLifecycle() lo único que debemos hacer es agregar esta dependencia en el build.gradle

...
dependencies {
  implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0" // La versión puede cambiar
}

Luego, en tu Composable, simplemente cambia collectAsState() por collectAsStateWithLifecycle():

@Composable
fun MyComposableScreen(
myViewModel: MyViewModel = hiltViewModel(),
) {

  val myState = myViewModel.myStateFlow.collectAsStateWithLifecycle()

  // Aquí podemos usar myState como nos de la gana, por ejemplosupongamos que myViewModel descarga un usuario cuando se inicia y luego guarda ese usuario en myState, pues podríamos pintar el correo del usuario
  Text(
   text = myState.value.user.email
  )
}

Publicado por Jesus Almaral

Soy ingeniero en Mecatrónica con maestría en Machine Learning, tengo experiencia en lenguajes como Java, Kotlin, Matlab, Android, Python, etc. Actualmente soy desarrollador de aplicaciones móviles, me gusta la música y toco la guitarra, me gusta mucho saber cosas sobre el universo, leer y comer tacos. También me apasiona enseñar.

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.