Rede no Android usando Corutin e Retrofit

Quanto mais eu lia e assistia aos relatórios sobre corotinas no Kotlin, mais eu admirava essa ferramenta de linguagem. Sua versão estável foi lançada recentemente no Kotlin 1.3, o que significa que é hora de iniciar o mergulho e tentar as corotinas em ação usando meu código RxJava existente como exemplo. Neste post, focaremos em como receber solicitações de rede existentes e convertê-las, substituindo RxJava por corotinas.



Francamente, antes de experimentar as corotinas, pensei que eram muito diferentes do que costumavam ser. No entanto, o princípio básico da corutina inclui os mesmos conceitos aos quais estamos acostumados nos fluxos reativos do RxJava. Como exemplo, vamos dar uma configuração simples do RxJava para criar uma solicitação de rede a partir de um dos meus aplicativos:


  • Defina a interface de rede para o Retrofit usando o adaptador Rx ( retrofit2: adapter-rxjava2 ). Funções retornarão objetos de uma estrutura Rx, como Único ou Observável . (As funções são usadas aqui e não métodos, já que se assume que o código antigo também foi escrito no Kotlin. Bem, ou convertido do Java pelo Android Studio).
  • Chamamos uma determinada função de outra classe (por exemplo, um repositório ou atividade).
  • Determinamos os segmentos nos quais o Agendador será executado e retornamos o resultado (métodos .subscribeOn () e .observeOn () ).
  • Nós salvamos o link no objeto para cancelar a inscrição (por exemplo, em CompositeObservable).
  • Inscreva-se no fluxo de eventos.
  • Cancele a inscrição no fluxo, dependendo dos eventos do ciclo de vida da Atividade.

Este é o algoritmo básico para trabalhar com o Rx (sem levar em consideração as funções de mapeamento e os detalhes de outras manipulações de dados). Quanto ao corutin, o princípio não muda muito. O mesmo conceito, apenas a terminologia está mudando.


  • Definimos a interface de rede para o Retrofit usando o adaptador para a rotina . Funções retornarão objetos adiados da API Corutin.
  • Chamamos essas funções de outra classe (por exemplo, um repositório ou atividade). A única diferença: cada função deve ser marcada como suspensão .
  • Defina o expedidor que será usado para a rotina.
  • Nós salvamos o link no objeto Job para cancelar a inscrição.
  • Execute a corotina de qualquer maneira possível.
  • Cancelamos as rotinas, dependendo dos eventos do ciclo de vida da atividade.

Como você pode ver nas seqüências acima, o processo de execução de Rx e Corutin é muito semelhante. Se você não levar em consideração os detalhes da implementação, isso significa que podemos manter a abordagem que temos - apenas substituímos algumas coisas para tornar nossa implementação compatível com a rotina.



O primeiro passo que devemos dar é permitir que o Retrofit retorne objetos adiados . Objetos diferidos são futuros sem bloqueio que podem ser desfeitos, se necessário. Esses objetos são essencialmente um trabalho de rotina, que contém o valor do trabalho correspondente. O uso do tipo Adiado nos permite misturar a mesma ideia que Job, com a adição da capacidade de obter estados adicionais, como sucesso ou falha - o que o torna ideal para solicitações de rede.


Se você estiver usando o Retrofit com o RxJava, provavelmente está usando o RxJava Call Adapter Factory. Felizmente, Jake Worton escreveu seu equivalente para corotina .


Podemos usar esse adaptador de chamada no construtor Retrofit e implementar nossa interface Retrofit da mesma maneira que no 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) } 

Agora vamos dar uma olhada na interface do MyService, que é usada acima. Devemos substituir os tipos Observáveis ​​retornados por Adiado na interface Retrofit. Se costumava ser assim:


 @GET("some_endpoint") fun getData(): Observable<List<MyData>> 

Agora vamos substituí-lo por:


 @GET("some_endpoint") fun getData(): Deferred<List<MyData>> 

Cada vez que chamamos getData (), o objeto Adiado retornará para nós - um análogo do Job para solicitações de rede. Anteriormente, de alguma forma, chamamos essa função com RxJava:


 override fun getData(): Observable<List<MyData>> { return myService.getData() .map { result -> result.map { myDataMapper.mapFromRemote(it) } } } 

Nesse fluxo RxJava, chamamos nossa função de utilitário e, em seguida, aplicamos a operação de mapa da API RxJava com o mapeamento subsequente dos dados retornados da solicitação para algo usado na camada da interface do usuário. Isso mudará um pouco quando usarmos uma implementação com corotinas. Para iniciantes, nossa função deve ser suspensa (adiada), a fim de realizar uma operação lenta dentro do corpo da função. E para isso, a função de chamada também deve ser adiada. Uma função adiada não é bloqueada e pode ser controlada após ser chamada inicialmente. Você pode iniciar, pausar, retomar ou cancelar.


 override suspend fun getData(): List<MyData> { ... } 

Agora temos que chamar nossa função de utilitário. À primeira vista, estamos fazendo a mesma coisa, mas devemos lembrar que agora temos Adiado em vez de Observável .


 override suspend fun getData(): List<MyData> { val result = myService.getData() ... } 

Devido a essa alteração, não podemos mais usar a cadeia de operações de mapas da API RxJava. E mesmo neste momento, os dados não estão disponíveis para nós - só temos uma instância adiada. Agora devemos usar a função waitit () para aguardar o resultado da consulta e, em seguida, continuar executando o código dentro da função:


 override suspend fun getData(): List<MyData> { val result = myService.getData().await() ... } 

Nesse ponto, obtemos a solicitação concluída e os dados disponíveis para uso. Portanto, agora podemos executar operações de mapeamento:


 override suspend fun getData(): List<MyData> { val result = myService.getData().await() return result.map { myDataMapper.mapFromRemote(it) } } 

Pegamos nossa interface Retrofit junto com a classe de chamada e usamos corotinas. Agora queremos chamar esse código da nossa Atividade ou fragmentos e usar os dados que obtemos da rede.


Em nossa Atividade, começaremos criando um link para Job, no qual podemos atribuir nossa operação de rotina e, em seguida, usá-la para controlar, por exemplo, o cancelamento de uma solicitação, durante uma chamada onDestroy () .


 private var myJob: Job? = null override fun onDestroy() { myJob?.cancel() super.onDestroy() } 

Agora podemos atribuir algo à variável myJob. Vejamos nossa solicitação com corotinas:


 myJob = CoroutineScope(Dispatchers.IO).launch { val result = repo.getLeagues() withContext(Dispatchers.Main) { //do something with result } } 

Neste post, eu não gostaria de me aprofundar em Dispatchers ou executar operações em corotinas, pois esse é um tópico para outros posts. Em suma, o que acontece aqui:


  • Crie uma instância do CoroutineScope usando o IO Dispatcher como parâmetro. Esse expedidor é usado para executar operações de E / S de bloqueio, como solicitações de rede.
  • Iniciamos nossa rotina com a função de inicialização - essa função inicia uma nova rotina e retorna um link para uma variável do tipo Job.
  • Em seguida, usamos o link para nosso repositório para receber dados executando uma solicitação de rede.
  • No final, usamos o despachante Principal para fazer o trabalho no thread da interface do usuário. Aqui podemos mostrar os dados recebidos aos usuários.

No próximo post, o autor promete se aprofundar um pouco mais nos detalhes, mas o material atual deve ser suficiente para começar a estudar as corotinas.


Nesta postagem, substituímos a implementação RxJava das respostas Retrofit por objetos adiados da API Corutin. Chamamos essas funções para receber dados da rede e os exibimos em nossa atividade. Espero que você tenha visto o número de alterações necessárias para iniciar as corotinas e apreciado a simplicidade da API, especialmente ao ler e escrever código.


Nos comentários da postagem original, encontrei uma solicitação tradicional: mostre o código inteiro. Portanto, criei um aplicativo simples que, na inicialização, obtém a programação dos trens com a API Yandex. Schedules e a exibe no RecyclerView. Link: https://github.com/AndreySBer/RetrofitCoroutinesExample


Eu também gostaria de acrescentar que as corotinas parecem ser um substituto inferior para o RxJava, pois não oferecem um conjunto equivalente de operações para sincronizar threads. A esse respeito, vale a pena examinar a implementação do ReactiveX para o Kotlin: RxKotlin .


Se você usa o Android Jetpack, também encontrei um exemplo com Retrofit, coroutines, LiveData e MVVM: https://codinginfinite.com/kotlin-coroutine-call-adapter-retrofit/

Source: https://habr.com/ru/post/pt428994/


All Articles