«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.