Mientras más leía y veía los informes sobre las corutinas en Kotlin, más admiraba esta herramienta de lenguaje. Su lanzamiento estable se lanzó recientemente en Kotlin 1.3, lo que significa que es hora de comenzar la inmersión y probar las rutinas en acción utilizando mi código RxJava existente como ejemplo. En esta publicación, nos centraremos en cómo tomar las solicitudes de red existentes y convertirlas reemplazando RxJava con corutinas.

Francamente, antes de probar las corutinas, pensaba que eran muy diferentes de lo que eran antes. Sin embargo, el principio básico de la corutina incluye los mismos conceptos a los que estamos acostumbrados en los flujos reactivos de RxJava. Como ejemplo, tomemos una configuración simple de RxJava para crear una solicitud de red desde una de mis aplicaciones:
- Defina la interfaz de red para el Retrofit utilizando el adaptador Rx ( retrofit2: adapter-rxjava2 ). Las funciones devolverán objetos de un marco Rx, como Single u Observable . (En adelante, se utilizan funciones, no métodos, ya que se supone que el código anterior también se escribió en Kotlin. Bueno, o se convirtió de Java a través de Android Studio).
- Llamamos a cierta función de otra clase (por ejemplo, un repositorio o actividad).
- Determinamos para los subprocesos en qué Programador se ejecutarán y devolvemos el resultado (métodos .subscribeOn () y .observeOn () ).
- Guardamos el enlace al objeto para cancelar la suscripción (por ejemplo, en CompositeObservable).
- Suscríbase a la corriente de eventos.
- Darse de baja de la secuencia en función de los eventos del ciclo de vida de la actividad.
Este es el algoritmo básico para trabajar con Rx (sin tener en cuenta las funciones de mapeo y los detalles de otras manipulaciones de datos). En cuanto a la corutina, el principio no cambia mucho. El mismo concepto, solo la terminología está cambiando.
- Definimos la interfaz de red para el Retrofit usando el adaptador para la rutina . Las funciones devolverán objetos diferidos de la API de Corutin.
- Llamamos a estas funciones desde otra clase (por ejemplo, un repositorio o actividad). La única diferencia: cada función debe marcarse como suspender .
- Defina el despachador que se usará para la rutina.
- Guardamos el enlace al objeto Job para cancelar la suscripción.
- Ejecute la rutina de cualquier manera posible.
- Cancelamos las rutinas según los eventos del ciclo de vida de la Actividad.
Como puede ver en las secuencias anteriores, el proceso de ejecución de Rx y Corutin es muy similar. Si no tiene en cuenta los detalles de implementación, esto significa que podemos mantener el enfoque que tenemos: solo reemplazamos algunas cosas para que nuestra implementación sea amigable con la rutina.

El primer paso que debemos tomar es permitir que Retrofit devuelva objetos diferidos . Los objetos diferidos son futuros sin bloqueo que se pueden deshacer si es necesario. Estos objetos son esencialmente un trabajo de rutina, que contiene el valor para el trabajo correspondiente. El uso del tipo diferido nos permite mezclar la misma idea que Job, con la posibilidad de obtener estados adicionales, como éxito o fracaso, lo que lo hace ideal para solicitudes de red.
Si está utilizando Retrofit con RxJava, probablemente esté utilizando RxJava Call Adapter Factory. Afortunadamente, Jake Worton escribió su equivalente para la corutina .
Podemos usar este adaptador de llamadas en el generador de Retrofit, y luego implementar nuestra interfaz Retrofit de la misma manera que con RxJava:
private fun makeService(okHttpClient: OkHttpClient): MyService { val retrofit = Retrofit.Builder() .baseUrl("some_api") .client(okHttpClient) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() return retrofit.create(MyService::class.java) }
Ahora veamos la interfaz de MyService, que se usa arriba. Debemos reemplazar los tipos de Observable devueltos con Diferido en la interfaz Retrofit. Si solía ser así:
@GET("some_endpoint") fun getData(): Observable<List<MyData>>
Ahora lo reemplazamos con:
@GET("some_endpoint") fun getData(): Deferred<List<MyData>>
Cada vez que llamamos a getData (), el objeto diferido regresará a nosotros, un análogo de Job para solicitudes de red. Anteriormente, de alguna manera llamamos a esta función con RxJava:
override fun getData(): Observable<List<MyData>> { return myService.getData() .map { result -> result.map { myDataMapper.mapFromRemote(it) } } }
En esta secuencia de RxJava, llamamos a nuestra función de utilidad, luego aplicamos la operación de mapeo desde la API de RxJava con el mapeo posterior de los datos devueltos de la solicitud a algo utilizado en la capa de la interfaz de usuario. Esto cambiará un poco cuando usemos una implementación con corutinas. Para empezar, nuestra función debe suspenderse (diferirse) para realizar una operación diferida dentro del cuerpo de la función. Y para esto, la función de llamada también debe diferirse. Una función diferida no es de bloqueo, y se puede controlar después de que se llama inicialmente. Puede iniciarlo, pausarlo, reanudarlo o cancelarlo.
override suspend fun getData(): List<MyData> { ... }
Ahora tenemos que llamar a nuestra función de utilidad. A primera vista, estamos haciendo lo mismo, pero debemos recordar que ahora nos aplazamos en lugar de observables .
override suspend fun getData(): List<MyData> { val result = myService.getData() ... }
Debido a este cambio, ya no podemos usar la cadena de operación del mapa desde la API RxJava. E incluso en este punto, los datos no están disponibles para nosotros, solo tenemos una instancia diferida. Ahora debemos usar la función await () para esperar el resultado de la consulta y luego continuar ejecutando el código dentro de la función:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() ... }
En este punto, obtenemos la solicitud completa y los datos disponibles para su uso. Por lo tanto, ahora podemos realizar operaciones de mapeo:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() return result.map { myDataMapper.mapFromRemote(it) } }
Tomamos nuestra interfaz Retrofit junto con la clase de llamada y usamos corutinas. Ahora queremos llamar a este código desde nuestra Actividad o fragmentos y usar los datos que obtuvimos de la red.
En nuestra Actividad, comenzaremos creando un enlace a Job, en el que podemos asignar nuestra operación de rutina y luego usarla para controlar, por ejemplo, cancelar una solicitud, durante una llamada onDestroy () .
private var myJob: Job? = null override fun onDestroy() { myJob?.cancel() super.onDestroy() }
Ahora podemos asignar algo a la variable myJob. Veamos nuestra solicitud con corutinas:
myJob = CoroutineScope(Dispatchers.IO).launch { val result = repo.getLeagues() withContext(Dispatchers.Main) { //do something with result } }
En esta publicación, no me gustaría profundizar en Dispatchers o realizar operaciones dentro de las rutinas, ya que este es un tema para otras publicaciones. En resumen, lo que sucede aquí:
- Cree una instancia de CoroutineScope utilizando IO Dispatcher como parámetro. Este despachador se utiliza para realizar operaciones de bloqueo de E / S, como solicitudes de red.
- Iniciamos nuestra rutina con la función de inicio : esta función inicia una nueva rutina y devuelve un enlace a una variable de tipo Job.
- Luego usamos el enlace a nuestro repositorio para recibir datos al realizar una solicitud de red.
- Al final, usamos el despachador principal para hacer el trabajo en el hilo de la interfaz de usuario. Aquí podemos mostrar los datos recibidos a los usuarios.
En la próxima publicación, el autor promete profundizar un poco más en los detalles, pero el material actual debería ser suficiente para comenzar a estudiar las rutinas.
En esta publicación, reemplazamos la implementación RxJava de las respuestas de Retrofit con objetos diferidos de la API de Corutin. Llamamos a estas funciones para recibir datos de la red y luego mostrarlos en nuestra actividad. Espero que haya visto los pocos cambios que necesita hacer para comenzar con las rutinas, y haya apreciado la simplicidad de la API, especialmente al leer y escribir código.
En los comentarios de la publicación original, encontré una solicitud tradicional: mostrar el código completo. Por lo tanto, hice una aplicación simple que, al inicio, recibe un horario de trenes con Yandex. Schedules API y lo muestra en RecyclerView. Enlace: https://github.com/AndreySBer/RetrofitCoroutinesExample
También me gustaría agregar que las corutinas parecen ser un reemplazo inferior para RxJava, ya que no ofrecen un conjunto equivalente de operaciones para sincronizar hilos. En este sentido, vale la pena mirar la implementación de ReactiveX para Kotlin: RxKotlin .
Si usa Android Jetpack, también encontré un ejemplo con Retrofit, coroutines, LiveData y MVVM: https://codinginfinite.com/kotlin-coroutine-call-adapter-retrofit/