1. Обзор
В этой статье основное внимание уделяется тому, как реализовать простой REST-клиент с поддержкой RxJava с помощью Retrofit .
Мы создадим пример приложения, взаимодействующего с GitHub API, используя стандартный подход к модернизации, а затем усовершенствуем его с помощью RxJava, чтобы использовать преимущества реактивного программирования.
2. Обычная модернизация
Давайте сначала создадим пример с Retrofit. Мы будем использовать API-интерфейсы GitHub, чтобы получить отсортированный список всех участников, которые имеют более 100 вкладов в любом репозитории.
2.1. Зависимости Maven
Чтобы начать проект с Retrofit, давайте включим эти артефакты Maven:
<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>
Последние версии см. в разделе retrofit и convert-gson в репозитории Maven Central.
2.2. API-интерфейс
Создадим простой интерфейс:
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);
}
Метод listRepos()
извлекает список репозиториев для данного пользователя, переданного в качестве параметра пути.
Метод listRepoContributers()
извлекает список участников для данного пользователя и репозитория, которые передаются как параметры пути.
2.3. Логика
Давайте реализуем необходимую логику, используя объекты Retrofit Call
и обычный код Java:
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. Интеграция с RxJava
Retrofit позволяет нам получать результаты вызовов с помощью настраиваемых обработчиков вместо обычного объекта Call
с помощью адаптеров Retrofit Call .
Это позволяет использовать здесь RxJava Observables
и Flowables
.
3.1. Зависимости Maven
Чтобы использовать адаптер RxJava, нам нужно включить этот артефакт Maven:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava</artifactId>
<version>2.3.0</version>
</dependency>
Для получения последней версии проверьте adapter-rxjava в центральном репозитории Maven.
3.2. Зарегистрировать адаптер вызова RxJava
Добавим в билдер RxJavaCallAdapter
:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
3.3. API-интерфейс
На этом этапе мы можем изменить возвращаемый тип методов интерфейса, чтобы использовать Observable<…>
вместо Call<…>
. Мы можем использовать другие типы Rx, такие как Observable
, Flowable
, Single
, Maybe
, Completable
.
Давайте изменим наш интерфейс API, чтобы использовать 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. Логика
Давайте реализуем это с помощью 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. Вывод
Сравнив код до и после использования RxJava, мы обнаружили, что он был улучшен следующим образом:
- Реактивный — поскольку наши данные теперь передаются потоками, это позволяет нам выполнять асинхронную потоковую обработку с неблокирующим обратным давлением.
- Ясно - из-за его декларативного характера
- Краткость – вся операция может быть представлена как одна цепочка операций
Весь код в этой статье доступен на GitHub.
Пакет com.foreach.retrofit.basic
содержит базовый пример модернизации, а пакет com.foreach.retrofit.
rx содержит пример модернизации с интеграцией RxJava.