Hacer consciente el ciclo de vida de Cold Streams | Autor: Hicham Boushaba | Noviembre de 2021



Con la introducción de SharedFlow y StateFlow, muchos desarrolladores están migrando desde LiveDataelectrónico Capa de interfaz de usuario para aprovechar la API de Flow e implementar una API más consistente en todas las capas, pero desafortunadamente, como explicó Christophe Beyls en su publicación, cuando el ciclo de vida de la vista entra en la ecuación, la migración es complicada. Versión 2.4 lifecycle:lifecycle-runtime-ktx
Se han introducido API para ayudar en este sentido: repeatOnLifecycle
con flowWithLifecycle
(Para obtener más información, consulte el artículo: Una forma más segura de recopilar transmisiones desde la interfaz de usuario de Android), en este artículo, intentaremos usarlas, discutiremos un pequeño problema que introducen en algunos casos, vemos Ver si podemos idear una solución más flexible.
Para explicar el problema, imaginemos que tenemos una aplicación de muestra que escucha las actualizaciones de ubicación cuando está activa, y cada vez que hay una nueva ubicación disponible, realiza llamadas a la API para recuperar algunas ubicaciones cercanas.Por lo tanto, para monitorear las actualizaciones de ubicación, escribiremos una clase LocationObserver que proporcione un flujo frío que las devuelva.
Entonces usaremos esta clase en nuestro ViewModel
Por simplicidad, usamos
AndroidViewModel
accesoContext
Directamente, no nos ocuparemos de diferentes casos extremos con respecto a los permisos y la configuración de ubicación.
Ahora, lo que tenemos que hacer en Fragmento es escuchar el derecho viewState
Actualice y actualice la interfaz de usuario:
dónde FragmentMainBinding#render
Es una extensión que puede actualizar la interfaz de usuario.
Ahora, si intentamos ejecutar la aplicación, cuando la ponemos en segundo plano, veremos que LocationObserver todavía está escuchando actualizaciones de ubicación y luego obtenemos ubicaciones cercanas, incluso si la interfaz de usuario las ignora.
Nuestro primer intento de resolver este problema fue utilizar la nueva API flowWithLifecycle
Si ejecutamos la aplicación ahora, notaremos que imprime la siguiente línea en Logcat cada vez que entra en segundo plano.
D/LocationObserver: stop observing location updates
Entonces la nueva API resuelve este problema, pero hay un problema. Siempre que la aplicación entre en segundo plano y regresemos, perderemos los datos que teníamos antes. Incluso si la ubicación no cambia, volveremos a hacer clic en la API. Esto sucede porque flowWithLifecycle
Upstream se cancelará cada vez que se utilice lifecycle
Pase a continuación State
(Este es Started
Para nosotros) y reinícielo de nuevo cuando se restaure el estado.
La solución oficial que se sigue utilizando flowWithLifecycle
Como se explica en el artículo de José Alcérreca, utiliza stateIn
Pero hay un especial timeout
Establezca en 5 segundos para considerar los cambios de configuración, por lo que debemos agregar la siguiente declaración al flujo de nuestro viewState
Esto funciona bien, excepto que detener / reiniciar Flow cada vez que la aplicación ingresa en segundo plano creará otro problema, por ejemplo, no necesitamos obtener la ubicación cercana a menos que la ubicación cambie la distancia mínima, así que cambiemos el código a la siguiente
Si ejecutamos la aplicación ahora, la ponemos en segundo plano durante más de 5 segundos y luego la volvemos a abrir, notaremos que incluso si la ubicación no cambia en absoluto, volveremos a adquirir ubicaciones cercanas, y al mismo tiempo, en La mayoría de los casos, esto no es un gran problema, y puede ser costoso en algunos casos: red lenta, API lenta o computación pesada …
Si podemos hacer nuestro locationUpdates
¿Transmitir conciencia del ciclo de vida, detenerlo sin ninguna interacción explícita de Fragment?De esta manera, podremos dejar de escuchar actualizaciones de ubicación sin tener que reiniciar todo el Flow. Si la ubicación no ha cambiado, entonces vuelve a ejecutar todos los operadores intermedios, e incluso podemos recopilar nuestro viewState
Stream se usa con regularidad launchWhenStarted
Porque nos aseguraremos de que no se ejecute porque no hemos emitido ninguna ubicación.
Si podemos tener un flujo de calor interno dentro del ViewModel, observemos el estado de la Vista:
Entonces podremos tener una extensión que detiene y reinicia nuestro flujo ascendente según el ciclo de vida:
De hecho, podemos usar LifecycleEventObserver
Interfaz del programa de aplicación
Podemos usarlo para conectarnos a los eventos del ciclo de vida de Fragment:
Con esto, ahora podemos actualizar nuestro locationUpdates
Fluir debajo
Podemos observar nuestro viewState
Fluya regularmente en Fragmento, no hay necesidad de preocuparse por mantener el GPS encendido cuando la aplicación ingresa en segundo plano
nombre de la extensión whenAtLeast
Es flexible porque se puede aplicar a cualquier Flow en la cadena, no solo durante el período de recolección, sino que, como hemos visto, aplicándolo al flujo de activación upstream (actualización de ubicación en nuestro caso), resultando en menos cálculos:
- A menos que sea necesario, los operadores intermedios, incluida la extracción de ubicaciones cercanas, no se ejecutarán.
- No volveremos a enviar los resultados a la interfaz de usuario cuando regresemos del fondo, porque no cancelaremos la recopilación.
Si desea ver el código completo en Github: https://github.com/hichamboushaba/FlowLifecycle, y el código completo incluye un ejemplo de cómo podemos usar estos cambios para probar nuestro ViewModel.
Como puede ver, usar Kotlin Flows en la capa de la interfaz de usuario todavía no es siempre tan simple, pero aún lo prefiero a LiveData, puede acceder a todas sus API y usar API consistentes en todas las capas. Para los problemas que estamos discutiendo aquí, personalmente, solo puse la lógica explicada en BaseFragment / BaseViewModel, y luego puedo usarla en todas las pantallas sin ningún texto repetitivo, y ha estado en mi aplicación personal desde 2018 El programa funciona bien. Esperemos que esta solicitud de función se implemente para proporcionar una forma de cooperar para suspender la recopilación sin cancelarla, lo que resolverá por completo el problema.
¿Qué opinas de la solución explicada en este artículo? ¿Perdí una solución más simple a este problema? Déjame saber los comentarios.