1. Overview
This article focuses on how to implement a simple RxJava-ready REST Client using Retrofit.
We’ll build an example application interacting with the GitHub API – using the standard Retrofit approach, and then we’ll enhance it using RxJava to leverage the advantages of Reactive Programming.
2. Plain Retrofit
Let’s first build an example with Retrofit. We’ll use the GitHub APIs to get a sorted list of all the contributors that have more than 100 contributions in any repository.
2.1. Maven Dependencies
To start a project with Retrofit, let’s include these Maven artifacts:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.3.0</version>
</dependency>
For the latest versions, have a look at retrofit and converter-gson on Maven Central repository.
2.2. API Interface
Let’s create a simple interface:
public interface GitHubBasicApi {
@GET("users/{user}/repos")
Call<List> listRepos(@Path("user") String user);
@GET("repos/{user}/{repo}/contributors")
Call<List> listRepoContributors(
@Path("user") String user,
@Path("repo") String repo);
}
The listRepos() method retrieves a list of repositories for a given user passed as a path parameter.
The listRepoContributers() method retrieves a list of contributors for a given user and repository, both passed as path parameters.
2.3. Logic
Let’s implement the required logic using Retrofit Call objects and normal Java code:
class GitHubBasicService {
private GitHubBasicApi gitHubApi;
GitHubBasicService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
gitHubApi = retrofit.create(GitHubBasicApi.class);
}
List<String> getTopContributors(String userName) throws IOException {
List<Repository> repos = gitHubApi
.listRepos(userName)
.execute()
.body();
repos = repos != null ? repos : Collections.emptyList();
return repos.stream()
.flatMap(repo -> getContributors(userName, repo))
.sorted((a, b) -> b.getContributions() - a.getContributions())
.map(Contributor::getName)
.distinct()
.sorted()
.collect(Collectors.toList());
}
private Stream<Contributor> getContributors(String userName, Repository repo) {
List<Contributor> contributors = null;
try {
contributors = gitHubApi
.listRepoContributors(userName, repo.getName())
.execute()
.body();
} catch (IOException e) {
e.printStackTrace();
}
contributors = contributors != null ? contributors : Collections.emptyList();
return contributors.stream()
.filter(c -> c.getContributions() > 100);
}
}
3. Integrating With RxJava
Retrofit lets us receive calls results with custom handlers instead of the normal Call object by using Retrofit Call adapters. This makes it possible to use RxJava Observables and Flowables here.
3.1. Maven Dependencies
To use RxJava adapter, we need to include this Maven artifact:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava</artifactId>
<version>2.3.0</version>
</dependency>
For the latest version please check adapter-rxjava in Maven central repository.
3.2. Register RxJava Call Adapter
Let’s add RxJavaCallAdapter to the builder:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
3.3. API Interface
At this point, we can change the return type of interface methods to use Observable<…> rather than Call<…>. We may use other Rx types like Observable, Flowable, Single, Maybe, Completable.
Let’s modify our API interface to use Observable:
public interface GitHubRxApi {
@GET("users/{user}/repos")
Observable<List<Repository>> listRepos(@Path("user") String user);
@GET("repos/{user}/{repo}/contributors")
Observable<List<Contributer>> listRepoContributors(
@Path("user") String user,
@Path("repo") String repo);
}
3.4. Logic
Let’s implement it using RxJava:
class GitHubRxService {
private GitHubRxApi gitHubApi;
GitHubRxService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
gitHubApi = retrofit.create(GitHubRxApi.class);
}
Observable<String> getTopContributors(String userName) {
return gitHubApi.listRepos(userName)
.flatMapIterable(x -> x)
.flatMap(repo -> gitHubApi.listRepoContributors(userName, repo.getName()))
.flatMapIterable(x -> x)
.filter(c -> c.getContributions() > 100)
.sorted((a, b) -> b.getContributions() - a.getContributions())
.map(Contributor::getName)
.distinct();
}
}
4. Conclusion
Comparing the code before and after using RxJava, we’ve found that it has been improved in the following ways:
- Reactive – as our data now flows in streams, it enables us to do asynchronous stream processing with non-blocking back pressure
- Clear – due to its declarative nature
- Concise – the whole operation can be represented as one operation chain
All the code in this article is available over on GitHub.
The package com.baeldung.retrofit.basic contains the basic retrofit example while the package *com.baeldung.retrofit.*rx contains the retrofit example with RxJava integration.