«1. Обзор
В этой статье рассказывается, как реализовать простой REST-клиент с поддержкой RxJava с помощью Retrofit.
Мы создадим пример приложения, взаимодействующего с GitHub API, используя стандартный подход к модернизации, а затем усовершенствуем его с помощью RxJava, чтобы использовать преимущества реактивного программирования.
2. Обычная модификация
Давайте сначала создадим пример с модификацией. Мы будем использовать 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.
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 Адаптеры вызовов. Это позволяет использовать здесь 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 Call Adapter
Добавим RxJavaCallAdapter в сборщик:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
3.3. Интерфейс API
На этом этапе мы можем изменить возвращаемый тип методов интерфейса, чтобы использовать Observable\u003c…\u003e, а не Call\u003c…\u003e. Мы можем использовать другие типы 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.baeldung.retrofit.basic содержит базовый пример модернизации, а пакет com.baeldung.retrofit.rx содержит пример модернизации с интеграцией RxJava.