Введение в RxJava

1. Обзор

В этой статье мы собираемся сосредоточиться на использовании Reactive Extensions (Rx) в Java для составления и использования последовательностей данных.

На первый взгляд, API может выглядеть похожим на Java 8 Streams, но на самом деле он гораздо более гибкий и гибкий, что делает его мощной парадигмой программирования.

Если вы хотите узнать больше о RxJava, просмотрите ссылку:/rxjava-backpressure[это рецензирование].

2. Настроить

Чтобы использовать RxJava в нашем проекте Maven , нам нужно добавить следующую зависимость в наш pom .xml:

<dependency>
    <groupId>io.reactivex</groupId>
    <artifactId>rxjava</artifactId>
    <version>${rx.java.version}</version>
</dependency>

Или для проекта Gradle:

compile 'io.reactivex.rxjava:rxjava:x.y.z'

3. Функциональные реактивные концепции

С одной стороны, функциональное программирование - это процесс создания программного обеспечения путем составления чистых функций, избегая общего состояния, изменяемых данных и побочных эффектов.

С другой стороны, reactive program - это парадигма асинхронного программирования, связанная с потоками данных и распространением изменений.

Вместе функциональное реактивное программирование образует комбинацию функциональных и реактивных методов, которые могут представлять собой элегантный подход к программированию, управляемому событиями, со значениями, которые меняются со временем и где потребитель реагирует на данные по мере их поступления.

Эта технология объединяет различные реализации ее основных принципов, некоторые авторы придумали документ, который определяет общий словарь для описания нового типа приложений.

3.1. Реактивный Манифест

Http://www.reactivemanifesto.org/[Reactive Manifesto]- это онлайн-документ, который устанавливает высокий стандарт для приложений в индустрии разработки программного обеспечения. Проще говоря, реактивные системы это:

  • Отзывчивый - системы должны отвечать своевременно

  • Message Driven - системы должны использовать асинхронную передачу сообщений между

компоненты для обеспечения слабого сцепления ** Эластичный - системы должны оставаться отзывчивыми при высокой нагрузке

  • Устойчивость - системы должны оставаться отзывчивыми при сбое некоторых компонентов

4. Наблюдаемые

При работе с Rx нужно понимать два ключевых типа:

  • Observable представляет любой объект, который может получить данные из данных

источник и состояние которого может представлять интерес таким образом, что другие объекты может зарегистрировать интерес ** Observer - это любой объект, который хочет получить уведомление, когда государство

другого объекта изменений

Observer подписывается на последовательность Observable . Последовательность отправляет элементы observer по одному.

Observer обрабатывает каждый перед обработкой следующего. Если многие события поступают асинхронно, они должны быть сохранены в очереди или удалены.

В Rx observer никогда не будет вызываться с неупорядоченным элементом или вызываться до возврата обратного вызова для предыдущего элемента.

4.1. Типы Observable

Есть два типа:

  • Non-Blocking – асинхронное выполнение поддерживается и разрешено

отписаться в любой точке потока событий. В этой статье мы сосредоточимся в основном на такого рода

  • Blocking – все onNext вызовы наблюдателя будут синхронными, и это

невозможно отписаться в середине потока событий. Мы всегда можем преобразовать Observable в Blocking Observable , используя метод toBlocking:

BlockingObservable<String> blockingObservable = observable.toBlocking();

4.2. Операторы

  • Operator - это функция, которая принимает один Observable (источник) в качестве первого аргумента и возвращает другой Observable (пункт назначения). ** Затем для каждого элемента, который испускает исходная наблюдаемая, она применяет функцию к этому элементу и затем генерирует результат по назначению Observable .

Operators могут быть объединены в цепочку для создания сложных потоков данных, которые фильтруют события на основе определенных критериев. Несколько операторов могут быть применены к тому же observable .

Нетрудно попасть в ситуацию, когда Observable испускает элементы быстрее, чем operator или observer может их поглотить.

Вы можете прочитать больше о ссылке обратного давления:/rxjava-backpressure[здесь.]

4.3. Создать наблюдаемый

Базовый оператор just создает Observable , который перед завершением генерирует один универсальный экземпляр, String «Hello» . Когда мы хотим получить информацию из Observable , мы реализуем интерфейс observer и затем вызываем подписку для требуемого Observable:

Observable<String> observable = Observable.just("Hello");
observable.subscribe(s -> result = s);

assertTrue(result.equals("Hello"));

4.4. OnNext, OnError, и OnCompleted

В интерфейсе observer есть три метода, о которых мы хотим знать:

, OnNext вызывается на нашем observer каждый раз, когда новое событие

опубликовано в прикрепленном Observable . Это метод, в котором мы будем выполнить какое-либо действие для каждого события , OnCompleted вызывается, когда последовательность событий, связанных с

Observable завершен, указывая, что мы не должны ожидать больше onNext призывает нашего наблюдателя , OnError вызывается, когда во время

RxJava код платформы или наш код обработки событий

Возвращаемым значением для метода Observables subscribe является интерфейс subscribe :

String[]letters = {"a", "b", "c", "d", "e", "f", "g"};
Observable<String> observable = Observable.from(letters);
observable.subscribe(
  i -> result += i, //OnNext
  Throwable::printStackTrace,//OnError
  () -> result += "__Completed"//OnCompleted
);
assertTrue(result.equals("abcdefg__Completed"));

5. Наблюдаемые преобразования и условные операторы

5.1. Карта

Оператор m _ap преобразует элементы, испускаемые Observable_ , применяя функцию к каждому элементу.

Давайте предположим, что есть объявленный массив строк, который содержит несколько букв из алфавита, и мы хотим напечатать их в заглавном режиме:

Observable.from(letters)
  .map(String::toUpperCase)
  .subscribe(letter -> result += letter);
assertTrue(result.equals("ABCDEFG"));

The flatMap может использоваться для выравнивания Observables всякий раз, когда мы в конечном итоге с вложенными Observables.

Более подробную информацию о разнице между map и flatMap можно найти по ссылке:/java-Difference-Map-and-Flatmap[здесь].

Предполагая, что у нас есть метод, который возвращает Observable <String> из списка строк. Теперь мы будем печатать для каждой строки из нового Observable списка заголовков на основе того, что Subscriber видит:

Observable<String> getTitle() {
    return Observable.from(titleList);
}
Observable.just("book1", "book2")
  .flatMap(s -> getTitle())
  .subscribe(l -> result += l);

assertTrue(result.equals("titletitle"));

5.2. Scan

Оператор _scan a применяет функцию к каждому элементу, последовательно генерируемому Observable_ , и генерирует каждое последующее значение.

Это позволяет нам переносить состояние от события к событию:

String[]letters = {"a", "b", "c"};
Observable.from(letters)
  .scan(new StringBuilder(), StringBuilder::append)
  .subscribe(total -> result += total.toString());
assertTrue(result.equals("aababc"));

5.3. Группа по

Оператор Group by позволяет классифицировать события на входе Observable в выходные категории.

Предположим, что мы создали массив целых чисел от 0 до 10, а затем применили group by , который разделит их на категории even и odd :

Observable.from(numbers)
  .groupBy(i -> 0 == (i % 2) ? "EVEN" : "ODD")
  .subscribe(group ->
    group.subscribe((number) -> {
        if (group.getKey().toString().equals("EVEN")) {
            EVEN[0]+= number;
        } else {
            ODD[0]+= number;
        }
    })
  );
assertTrue(EVEN[0].equals("0246810"));
assertTrue(ODD[0].equals("13579"));

5.4. Фильтр

Оператор filter выдает только те элементы из observable , которые проходят predicate тест

Итак, давайте отфильтруем целочисленный массив для нечетных чисел:

Observable.from(numbers)
  .filter(i -> (i % 2 == 1))
  .subscribe(i -> result += i);

assertTrue(result.equals("13579"));

5.5. Условные операторы

DefaultIfEmpty испускает элемент из источника Observable или элемент по умолчанию, если источник Observable пуст:

Observable.empty()
  .defaultIfEmpty("Observable is empty")
  .subscribe(s -> result += s);

assertTrue(result.equals("Observable is empty"));

Следующий код генерирует первую букву алфавита ‘ a’ , потому что массив letters не пустой, и это то, что он содержит в первой позиции:

Observable.from(letters)
  .defaultIfEmpty("Observable is empty")
  .first()
  .subscribe(s -> result += s);

assertTrue(result.equals("a"));

Оператор TakeWhile удаляет элементы, испускаемые Observable после того, как указанное условие становится ложным

Observable.from(numbers)
  .takeWhile(i -> i < 5)
  .subscribe(s -> sum[0]+= s);

assertTrue(sum[0]== 10);

Конечно, есть и другие операторы, которые могут удовлетворить наши потребности, такие как __Contain, SkipWhile, SkipUntil, TakeUntil и т. Д.

6. Подключаемые Наблюдаемые

  • ConnectableObservable напоминает обычный Observable , за исключением того, что он не начинает испускать элементы, когда на него подписаны, а только когда к нему применяется оператор connect . **

Таким образом, мы можем подождать, пока все предполагаемые наблюдатели подпишутся на Observable , прежде чем Observable начнет излучать элементы:

String[]result = {""};
ConnectableObservable<Long> connectable
  = Observable.interval(200, TimeUnit.MILLISECONDS).publish();
connectable.subscribe(i -> result[0]+= i);
assertFalse(result[0].equals("01"));

connectable.connect();
Thread.sleep(500);

assertTrue(result[0].equals("01"));

7. Холост

Single похож на Observable , который вместо генерации серии значений выдает одно значение или уведомление об ошибке.

С этим источником данных мы можем использовать только два метода подписки:

  • OnSuccess возвращает Single , который также вызывает метод, который мы указываем

  • OnError также возвращает Single , который немедленно уведомляет

подписчики ошибки

String[]result = {""};
Single<String> single = Observable.just("Hello")
  .toSingle()
  .doOnSuccess(i -> result[0]+= i)
  .doOnError(error -> {
      throw new RuntimeException(error.getMessage());
  });
single.subscribe();

assertTrue(result[0].equals("Hello"));

8. Предметы

Subject - это одновременно два элемента, subscriber и observable . Как подписчик, субъект может использоваться для публикации событий, происходящих из нескольких наблюдаемых.

И поскольку это также можно наблюдать, события от нескольких подписчиков могут быть пересланы как события для всех, кто их наблюдает.

В следующем примере мы рассмотрим, как наблюдатели смогут видеть события, которые происходят после их подписки:

Integer subscriber1 = 0;
Integer subscriber2 = 0;
Observer<Integer> getFirstObserver() {
    return new Observer<Integer>() {
        @Override
        public void onNext(Integer value) {
           subscriber1 += value;
        }

        @Override
        public void onError(Throwable e) {
            System.out.println("error");
        }

        @Override
        public void onCompleted() {
            System.out.println("Subscriber1 completed");
        }
    };
}

Observer<Integer> getSecondObserver() {
    return new Observer<Integer>() {
        @Override
        public void onNext(Integer value) {
            subscriber2 += value;
        }

        @Override
        public void onError(Throwable e) {
            System.out.println("error");
        }

        @Override
        public void onCompleted() {
            System.out.println("Subscriber2 completed");
        }
    };
}

PublishSubject<Integer> subject = PublishSubject.create();
subject.subscribe(getFirstObserver());
subject.onNext(1);
subject.onNext(2);
subject.onNext(3);
subject.subscribe(getSecondObserver());
subject.onNext(4);
subject.onCompleted();

assertTrue(subscriber1 + subscriber2 == 14)

9. Управление ресурсами

Операция Using позволяет нам связывать ресурсы, такие как соединение с базой данных JDBC, сетевое соединение или открытые файлы, с нашими наблюдаемыми объектами.

Здесь мы представляем в комментариях шаги, которые нам нужно сделать для достижения этой цели, а также пример реализации:

String[]result = {""};
Observable<Character> values = Observable.using(
  () -> "MyResource",
  r -> {
      return Observable.create(o -> {
          for (Character c : r.toCharArray()) {
              o.onNext(c);
          }
          o.onCompleted();
      });
  },
  r -> System.out.println("Disposed: " + r)
);
values.subscribe(
  v -> result[0]+= v,
  e -> result[0]+= e
);
assertTrue(result[0].equals("MyResource"));

10. Заключение

В этой статье мы поговорили о том, как использовать библиотеку RxJava, а также о том, как исследовать ее наиболее важные функции.

Полный исходный код проекта, включая все примеры кода, используемые здесь, можно найти по адресу over на Github .