RxJava 2 - Текучий

RxJava 2 - Текучий

1. Вступление

RxJava is a Reactive Extensions Java implementation that allows us to write event-driven, and asynchronous applications. Более подробную информацию о том, как использовать RxJava, можно найти в нашемintro article here.

RxJava 2 был переписан с нуля, что принесло множество новых функций; некоторые из которых были созданы как ответ на проблемы, которые существовали в предыдущей версии платформы.

Одна из таких функций -io.reactivex.Flowable.

2. Observable против. Flowable

В предыдущей версии RxJava был только один базовый класс для работы с источниками, учитывающими обратное давление и не поддерживающими обратное давление -Observable.

RxJava 2 ввел четкое различие между этими двумя типами источников - источники с поддержкой обратного давления теперь представлены с использованием специального класса -Flowable.

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

Кроме того, если мы имеем дело с большим количеством элементов, могут возникнуть два возможных сценария, связанных сbackpressure, в зависимости от типаObservable.

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

Однако при использованииhotObservable он будет продолжать генерировать события, даже если потребитель не успевает.

3. СозданиеFlowable

Есть разные способы создатьFlowable. Для нас удобно, что эти методы похожи на методы вObservable в первой версии RxJava.

3.1. ПростойFlowable

Мы можем создатьFlowable, используя методjust(), так же, как мы могли бы сObservable :

Flowable integerFlowable = Flowable.just(1, 2, 3, 4);

Несмотря на то, что использоватьjust() довольно просто, созданиеFlowable из статических данных не очень распространено, и оно используется в целях тестирования.

3.2. Flowable изObservable

When we have an Observable we can easily transform it to Flowable using the toFlowable() method:

Observable integerObservable = Observable.just(1, 2, 3);
Flowable integerFlowable = integerObservable
  .toFlowable(BackpressureStrategy.BUFFER);

Обратите внимание, что для того, чтобы выполнить преобразование, нам нужно обогатитьObservableBackpressureStrategy.. Мы опишем доступные стратегии в следующем разделе.

3.3. Flowable изFlowableOnSubscribe

RxJava 2 представил функциональный интерфейсFlowableOnSubscribe, который представляетFlowable, который начинает генерировать события после того, как потребитель подписывается на него.

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

Когда у нас естьFlowableOnSubscribe, мы можем использовать его для созданияFlowable:

FlowableOnSubscribe flowableOnSubscribe
 = flowable -> flowable.onNext(1);
Flowable integerFlowable = Flowable
  .create(flowableOnSubscribe, BackpressureStrategy.BUFFER);

В документации описано еще много методов для созданияFlowable.

4. FlowableBackpressureStrategy

Некоторые методы, такие какtoFlowable() илиcreate(), принимаютBackpressureStrategy в качестве аргумента.

BackpressureStrategy - это перечисление, которое определяет поведение противодавления, которое мы будем применять к нашему Текучий.

Он может кэшировать или удалять события или вообще не реализовывать какое-либо поведение, в последнем случае мы будем нести ответственность за его определение с помощью операторов обратного давления.

BackpressureStrategy похож наBackpressureMode, присутствующий в предыдущей версии RxJava.

В RxJava 2 доступно пять различных стратегий.

4.1. буфер

If we use the BackpressureStrategy.BUFFER, the source will buffer all the events until the subscriber can consume them:

public void thenAllValuesAreBufferedAndReceived() {
    List testList = IntStream.range(0, 100000)
      .boxed()
      .collect(Collectors.toList());

    Observable observable = Observable.fromIterable(testList);
    TestSubscriber testSubscriber = observable
      .toFlowable(BackpressureStrategy.BUFFER)
      .observeOn(Schedulers.computation()).test();

    testSubscriber.awaitTerminalEvent();

    List receivedInts = testSubscriber.getEvents()
      .get(0)
      .stream()
      .mapToInt(object -> (int) object)
      .boxed()
      .collect(Collectors.toList());

    assertEquals(testList, receivedInts);
}

Это похоже на вызов методаonBackpressureBuffer() дляFlowable,, но он не позволяет явно определить размер буфера или действие onOverflow.

4.2. Drop

Мы можем использоватьBackpressureStrategy.DROP, чтобы отбросить события, которые нельзя использовать, вместо их буферизации.

Опять же, это похоже на использованиеonBackpressureDrop() наFlowable:

public void whenDropStrategyUsed_thenOnBackpressureDropped() {

    Observable observable = Observable.fromIterable(testList);
    TestSubscriber testSubscriber = observable
      .toFlowable(BackpressureStrategy.DROP)
      .observeOn(Schedulers.computation())
      .test();
    testSubscriber.awaitTerminalEvent();
    List receivedInts = testSubscriber.getEvents()
      .get(0)
      .stream()
      .mapToInt(object -> (int) object)
      .boxed()
      .collect(Collectors.toList());

    assertThat(receivedInts.size() < testList.size());
    assertThat(!receivedInts.contains(100000));
 }

4.3. Самый последний

ИспользованиеBackpressureStrategy.LATEST заставит источник сохранять только самые последние события, тем самым перезаписывая любые предыдущие значения, если потребитель не может поддерживать:

public void whenLatestStrategyUsed_thenTheLastElementReceived() {

    Observable observable = Observable.fromIterable(testList);
    TestSubscriber testSubscriber = observable
      .toFlowable(BackpressureStrategy.LATEST)
      .observeOn(Schedulers.computation())
      .test();

    testSubscriber.awaitTerminalEvent();
    List receivedInts = testSubscriber.getEvents()
      .get(0)
      .stream()
      .mapToInt(object -> (int) object)
      .boxed()
      .collect(Collectors.toList());

    assertThat(receivedInts.size() < testList.size());
    assertThat(receivedInts.contains(100000));
 }

BackpressureStrategy.LATEST and BackpressureStrategy.DROP выглядят очень похоже, когда мы смотрим на код.

ОднакоBackpressureStrategy.LATEST перезапишет элементы, которые наш подписчик не может обработать, и оставит только самые свежие, отсюда и название.

BackpressureStrategy.DROP,, с другой стороны, отбрасывает элементы, которые невозможно обработать. Это означает, что не обязательно генерировать новейшие элементы.

4.4. ошибка

Когда мы используемBackpressureStrategy.ERROR,, мы просто говорим, чтоwe don’t expect backpressure to occur. Следовательно, следует выдатьMissingBackpressureException, если потребитель не успевает за источником:

public void whenErrorStrategyUsed_thenExceptionIsThrown() {
    Observable observable = Observable.range(1, 100000);
    TestSubscriber subscriber = observable
      .toFlowable(BackpressureStrategy.ERROR)
      .observeOn(Schedulers.computation())
      .test();

    subscriber.awaitTerminalEvent();
    subscriber.assertError(MissingBackpressureException.class);
}

4.5. Отсутствует

Если мы используемBackpressureStrategy.MISSING, источник будет отправлять элементы без отбрасывания или буферизации.

Нижестоящий поток будет иметь дело с переполнением в этом случае:

public void whenMissingStrategyUsed_thenException() {
    Observable observable = Observable.range(1, 100000);
    TestSubscriber subscriber = observable
      .toFlowable(BackpressureStrategy.MISSING)
      .observeOn(Schedulers.computation())
      .test();
    subscriber.awaitTerminalEvent();
    subscriber.assertError(MissingBackpressureException.class);
}

В наших тестах мы исключаемMissingbackpressureException для стратегийERROR иMISSING. Поскольку они оба вызовут такое исключение при переполнении внутреннего буфера источника.

Однако стоит отметить, что у них обоих разные цели.

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

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

5. Резюме

В этом руководстве мы представили новый класс, представленный в RxJava2, который называетсяFlowable..

Чтобы найти дополнительную информацию о самомFlowable и его API, мы можем обратиться кdocumentation.

Как всегда можно найти все образцы кодаover on GitHub.