Sincronizando chamadas RESTful com RxJava

Evandro F. Souza
Training Center
Published in
5 min readJan 3, 2019

A maioria dos aplicativos Android funciona com base em chamadas de APIs RESTful. À medida que o aplicativo cresce, o mesmo acontece com a complexidade das chamadas e seus dados. Atualmente existem bibliotecas que proporcionam todas as funcionalidades básicas para gerenciar essas chamadas.

No entanto, para encadeamento e sequenciamento de eventos, que vão além de uma única chamada de API, você pode acabar escrevendo um código confuso e propenso a erros.

No post anterior falei sobre as bibliotecas ReactiveX e seus benefícios. Neste post vamos continuar vendo sobre programação reativa, mas dessa vez com um exemplo prático, demonstrando um dos casos de uso.

O problema

Vamos começar ilustrando um problema básico, existente em diversos aplicativos: Um consumidor que faz chamadas assíncronas para uma API.

A API que queremos consumir é esta aqui. Ela possui um endpoint para usuários e outro para locais. Nossa aplicação deve listar todos os usuários com o seu local. Ela deve imprimir algo como:

<nome do usuário> - <nome do local> 

Ou seja, precisaremos fazer duas chamadas para API, sendo que uma depende da outra. Primeiro, precisaremos chamar o endpoint dos usuários. Para cada usuário, devemos chamar o endpoint de locais para pegar o nome do mesmo.

Para facilitar a vida, vamos utilizar a biblioteca RetroFit para as chamadas de API. Abaixo vamos ver um exemplo de código sem utilizar o RxJava:

Vamos entender um pouco o código:

O RetroFit nos proporciona duas interfaces: Call e CallBack.

Call: O uso dela é possível notar nas interfaces declaradas PlacesService e UsersService.

CallBack: Utilizada para os casos que queira implementar assincronia. Ela exige a implementação de dois métodos: onResponse e onFailure. Neles devemos implementar a ação que o nossa aplicação tomara ao sucesso ou falha daquela requisição.

Note que estamos chamando o método listUsers do objeto userService. Neste método, o RetroFit está chamando o endpoint /users. Ao retornar os usuários com sucesso (OnResponse), está sendo percorrido cada usuário e mais uma chamada é feita (getPlaceById), dessa vez para o endpoint /places/<place_id>.

Perceba que somente neste código simples temos dois OnResponse aninhados. Imagine se no futuro precisarmos fazer outra chamada de algo que dependa do endpoint /places. O aninhamento aumentaria exponencialmente.

Abaixo um resumo dos problemas que este código possui:

  • Os Callbacks aninhados afetam a legibilidade.
  • A complexidade do código torna-o propenso a erros. Por exemplo, o que ocorre no código acima caso o response.body esteja nulo?
  • A chamada do endpoint para buscar os usuários, está fortemente acoplada á chamada para buscar os locais.

Acesse aqui caso queira olhar o código completo

A solução

Antes de iniciar a analisar o código, vamos esclarecer o que está sendo utilizado aqui:

  • RxJava + RxAndroid: Os exemplos deste post são um aplicativo, por isso estamos utilizando o RxAndroid para permitir gerenciarmos as threads de modo otimizado para o Android.
  • Retrofit Adapters: O Retrofit possui adapters para funcionar com o RxJava. Se quiser aprender um pouco mais sobre eles acesse aqui.

Agora sim, o exemplo abaixo demonstra como o RxJava pode nos ajudar:

A primeira diferença ao comparar com o exemplo anterior é que agora não há mais a necessidade das interfaces Call e CallBack. No lugar delas agora temos as interfaces Observable e Observer. Conceitos estes que foram falados neste post.

Note que com o uso do RxJava o código ficou sequencial. Agora é praticamente uma cadeia de comandos. Vamos analisar o que cada um deles faz:

.flatMapIterable(users -> users)

O retorno do método listUsers é uma lista de usuários. A partir deste momento, é possível tomar ações no conjunto dos itens. Como fazemos para operar os usuários de forma individual? É neste ponto que entra o flatMapIterable. Esta linha acima está transformando a lista de usuários em algo iterável para o RxJava.

.flatMap(user -> placesService.getPlaceById(user.placeId),
(user, place) -> user.name + " - " + place.title)

O flatMap (junto com o map) é talvez um dos operadores mais utilizados nas linguagens funcionais. No ReactiveX, ele transforma um Observable aplicando uma determinada função. Esta função em questão deve retornar também um Observable, ao fazer isso, o flatMap mescla ambos Observables .

Parece complicado? 😑

Para esclarecer melhor, vamos entender por que utilizamos ele no nosso caso. Lembre que agora possuímos dois Observables: um emitido pelo método listUsers e outro pelo getPlaceById. Note também que o getPlaceById depende do resultado do listUsers. O primeiro parâmetro do flatMap está retornando um novo Observable, com o objeto de place com base no código contido no usuário. O segundo parâmetro é um mapper, ali está sendo mapeado o que deve ser feito com o resultado de ambos Observables. No caso, estamos simplesmente transformando-os numa string.

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

No RxJava, utilizamos os Schedulers para desenvolver aplicações que utilizam threads. Um Scheduler pode ser pensado como um conjunto de threads (vamos chamar de thread pool), que gerencia uma ou mais threads. Sempre que o Scheduler precisar executar uma tarefa, ele pegará uma thread do pool e a executará nela.

Existem diversos tipos de Schedulers, não quero prolongar o post falando deles. Caso queira aprender mais sobre, este post é muito bom.

Nas linhas citadas acima, estamos informando qual o Scheduler queremos utilizar para o Observable e o Observer.

Observable: No subscribeOn estamos utilizando o Schedulers.io(). Ele é usado para trabalhos do tipo I/O que não usam muita CPU, por exemplo, interação com o sistema de arquivos, realização de chamadas de rede, interações com o banco de dados, etc. Perfeito para o nosso caso, já que este Observable possui a única responsabilidade de fazer as chamadas para a API e passar a informação adiante.

Observer: No observeOn definimos oAndroidSchedulers.mainThread(), ele é um scheduler da biblioteca RxAndroid, utilizamos ele para os casos que a thread precisa interagir com a UI do aplicativo Android. No nosso exemplo, estamos atualizando a UI com as informações recebidas.

.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
....
}
@Override
public void onNext(String s) {
....
}
@Override
public void onError(Throwable e) {
....
}
@Override
public void onComplete() {
....
}
});

O subscribe é onde definimos toda a lógica do que queremos fazer. Este método tem diversas assinaturas diferentes. Por questões didáticas eu preferi a que utiliza a interface Observer. Note que os métodos que a interface exige implementação são autoexplicativos. Ali devemos implementar o comportamento da ocorrência de cada destes eventos.

Para ver a solução completa acesse aqui.

Conclusão

Acredito que este exemplo tenha demonstrado como o ReactiveX pode ajudar você a adaptar facilmente sua aplicação as constantes mudanças. O uso dos conceitos Observer/Observable em conjunto com os operadores, tornam o resultado um código limpo e legível. Além do mais, a adição do Scheduler deixa simples algo que frequentemente tira o sono dos desenvolvedores, o gerenciamento de threads.

Para finalizar, outro ponto que gostei muito das bibliotecas ReactiveX é o fato delas estarem presentes em diversas linguagens e sempre seguindo o mesmo padrão. Isso quer dizer que, se você precisa flutuar entre diferentes linguagens, você ainda terá a facilidade de utilizar programação Reativa na maioria delas e reaproveitar esse conhecimento adquirido quase que completamente.

Caso queira continuar estudando sobre, abaixo deixo algumas referências que me ajudaram:

Se quiser trocar uma ideia ou entrar em contato comigo, pode me achar no Twitter(@e_ferreirasouza) ou Linkedin.

Grande abraço e até a próxima!

--

--