Nuestra experiencia en migrar desde RxJava | en Coroutines de Jasmine Villadarez | Weeronline | Mayo de 2021
Uno de los desafíos en el desarrollo de aplicaciones de Android es ejecutar tareas en segundo plano. Como desarrolladores de Android, tenemos varias opciones: AsyncTasks, Services, Jobs, etc. Luego está RxJava, una biblioteca para crear secuencias observables asincrónicas.
Durante años, RxJava ha sido el santo grial de las tareas en segundo plano en Android. Facilitó el cambio de hilos y la propagación de errores. También evitó el infierno de recuerdo. Sin embargo, la mayoría de las aplicaciones de Android solo necesitan realizar llamadas a la API a la vez, lo que significa que la mayoría de las aplicaciones no maximizarán todo el potencial de RxJava.
Nuestro uso de RxJava
Nuestro uso de RxJava fue fácil. Se usó para garantizar que las llamadas a la API se realizaran en el subproceso en segundo plano y no bloquearan la interfaz de usuario. No se utilizaron corrientes reactivas. Llamamos a una API, esperamos su respuesta y la mostramos en la pantalla.
Por que cambiar
El uso de corrutinas ofrece varias ventajas, p. Ej. B. un número menor de métodos que son relativamente fáciles de comprender para los desarrolladores jóvenes y la integración del jetpack. Sin embargo, nuestra principal preocupación es que nuestra fuente de datos o nuestro módulo de datos Kotlin-Multiplatform esté listo. Con las corrutinas de Kotlin, eliminamos nuestra dependencia de RxJava, que solo está disponible para plataformas JVM.
Nuestra base de código es relativamente pequeña. No es posible cambiar de una biblioteca a otra a la vez, especialmente cuando se integra RxJava. Nuestro uso de RxJava puede ser simple, pero todos tienen nuestras interfaces API, pruebas unitarias y modelos de vista. Observables
. Necesitábamos una forma de asegurarnos de que pudiéramos introducir corrutinas de forma segura sin preocuparnos por interrupciones en la producción.
Ahí es donde Flow
entra.Flow
tiene muchas características de extensión que pueden hacer que el cambio de RxJava sea indoloro. Nos permitió cambiar solo partes de nuestro código en corrutinas de trabajo y conectar Flow a través de RxJava.
Por ejemplo tenemos uno PlaceRepository
con una función getPlace
que da una espalda Observable<Place>
override fun getPlace(lat: Double, lon: Double): Observable<Place>
return flow
val place = placeApi.getPlaceInfo(
lat.roundTo(MAX_DECIMAL_PLACES),
lon.roundTo(MAX_DECIMAL_PLACES)
)
emit(place)
.asObservable()
Como puede ver en el ejemplo anterior, el asObservable()
Función de extensión transformada Flow
en un RxJavaObservable
. getPlace
Las personas que llaman no sabrán que ya está en uso Flow
.
Otro enfoque está disponible de inmediato Flow
como tipo de retorno y llamada asObservable
en el código de la persona que llama
override fun getPlace(lat: Double, lon: Double): Flow<Place>
return flow
val place = placeApi.getPlaceInfo(
lat.roundTo(MAX_DECIMAL_PLACES),
lon.roundTo(MAX_DECIMAL_PLACES)
)
emit(place)
private fun someCaller(lat: Double, lon: Double): Observable<Place>
return placeRepository.getPlace(lat, lon).asObservable()
Esto nos permitió cambiar fragmentos de código, pasar paso a paso a producción y verificar si hay algún problema antes de convertir completamente RxJava a RxJava. Flow
. Se ha reducido el alto riesgo de dañar la producción. Flow
también tiene la ventaja de tener funciones similares a RxJava
lo que hizo que la personalización fuera relativamente fácil.
fun getPlace(lat: Double, lon: Double)
viewModelScope.launch
placeRepository.getPlace(lat, lon)
.flowOn(Dispatchers.IO)
.collect
updatePlace()
.catch emitError()
La parada funciona completamente
Flow
nos permitió eliminar las dependencias de RxJava sin problemas. Pero el uso principal de Flow
sigue devolviendo una secuencia de resultados, similar a RxJava Observable
. Nuestro uso de Flow
o Observable
no requiere una secuencia de resultados; hacemos una llamada a la API y esperamos su respuesta. Para estar a propósito en nuestro código, lo hemos eliminado Flow
y simplificó nuestro código mediante el uso de funciones de suspensión.
suspend fun getPlace(lat: Double, lon: Double): Place
return placeApi.getPlaceInfo(lat, lon)
Llamador:
fun getPlace(lat: Double, lon: Double)
viewModelScope.launch(Dispatchers.IO)
val place = placeRepository.getPlace(lat, lon)
updatePlace(place)
La migración de una tecnología a otra siempre plantea algunos desafíos. Esto puede resultar abrumador, especialmente cuando se migran bases de código estrechamente acopladas. Encontramos algunos durante nuestra migración, pero los más notables son la gestión de despachadores y la notificación de errores.
Cambie de despachadores y ejecute pruebas unitarias en ellos
En la prueba unitaria de RxJava hay funciones a las que se pueden configurar todos los programadores immediate
. Sin embargo, este no es el caso de los despachadores de rutina. Nuestra solución para esto es crear un DispatcherProvider
Clase en la que sobrescribiremos su implementación durante las pruebas. No es una gran molestia, pero hay algo a considerar.
Propagación de errores
Quizás uno de los mayores desafíos que enfrentamos en esta migración es la propagación de errores. Con RxJava, la mayoría de los errores pueden ser detectados y manejados por onError
Función. Además, no hay necesidad de preocuparse de que se cancelen otros trabajos debido a la excepción de otros trabajos. Es diferente cuando se usan corrutinas. Es importante comprender dónde pueden ocurrir los errores y que estos errores se manejen correctamente para evitar que se cancelen todos los trabajos en segundo plano.
Las excepciones en las corrutinas de Martin Vivo nos han ayudado a comprender cómo los errores se transfieren de los trabajos de crianza a los trabajos de niños y viceversa.
Si bien esto nos retuvo un poco, encontramos que resultó en que fuéramos deliberados en nuestro código y nos volviéramos más conscientes de las excepciones en nuestro código.
Utilizando RxJava
, Flow
Las funciones que se pausan aún dependen de cómo las use. Siempre hay más de una forma de hacer las cosas. Después de migrar a las corrutinas, nuestro código se volvió más legible y, en mi opinión, más accesible para los desarrolladores jóvenes de Android, ya que la curva de aprendizaje empinada de RxJava no está en la imagen. También pudimos aumentar nuestra tasa de ausencia de choques a medida que el cambio a las corrutinas aumentó nuestra conciencia de dónde pueden ocurrir las excepciones. ¡Lo más importante es que nuestro código se acerca un paso más a la preparación multiplataforma de Kotlin!