RxJava 2 - Flutuável

RxJava 2 - Flutuável

1. Introdução

RxJava is a Reactive Extensions Java implementation that allows us to write event-driven, and asynchronous applications. Mais informações sobre como usar o RxJava podem ser encontradas em nossointro article here.

O RxJava 2 foi reescrito do zero, o que trouxe vários novos recursos; alguns dos quais foram criados como resposta a problemas existentes na versão anterior da estrutura.

Um desses recursos é oio.reactivex.Flowable.

2. Observable vs. Flowable

Na versão anterior do RxJava, havia apenas uma classe base para lidar com fontes com e sem contrapressão -Observable.

RxJava 2 introduziu uma distinção clara entre esses dois tipos de fontes - fontes com reconhecimento de contrapressão são agora representadas usando uma classe dedicada -Flowable.

Fontes deObservable não suportam contrapressão. Por isso, devemos usá-lo para fontes que meramente consumimos e não podemos influenciar.

Além disso, se estivermos lidando com um grande número de elementos, dois cenários possíveis conectados combackpressure podem ocorrer dependendo do tipo deObservable.

No caso de usar o chamadocoldObservable“,, os eventos são emitidos preguiçosamente, então estamos seguros de transbordar um observador.

Ao usar umhotObservable, no entanto, ele continuará a emitir eventos, mesmo que o consumidor não consiga acompanhar.

3. Criando umFlowable

Existem diferentes maneiras de criar umFlowable. Convenientemente para nós, esses métodos parecem semelhantes aos métodos emObservable na primeira versão do RxJava.

3.1. Flowable simples

Podemos criar umFlowable usando o métodojust() da mesma forma que poderíamos comObservable :

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

Mesmo que usarjust() seja bastante simples, não é muito comum criar umFlowable a partir de dados estáticos e é usado para fins de teste.

3.2. Flowable deObservable

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);

Observe que para ser capaz de realizar a conversão, precisamos enriquecer oObservable com umBackpressureStrategy.. Descreveremos as estratégias disponíveis na próxima seção.

3.3. Flowable deFlowableOnSubscribe

RxJava 2 introduziu uma interface funcionalFlowableOnSubscribe, que representa umFlowable que começa a emitir eventos depois que o consumidor se inscreve.

Por isso, todos os clientes receberão o mesmo conjunto de eventos, o que tornaFlowableOnSubscribe seguro contra contrapressão.

Quando temos oFlowableOnSubscribe, podemos usá-lo para criar oFlowable:

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

A documentação descreve muitos outros métodos para criarFlowable.

4. FlowableBackpressureStrategy

Alguns métodos comotoFlowable() oucreate() usamBackpressureStrategy como argumento.

OBackpressureStrategy é uma enumeração, que define o comportamento de contrapressão que aplicaremos ao nosso Fluível.

Ele pode armazenar em cache ou descartar eventos ou não implementar nenhum comportamento; no último caso, seremos responsáveis ​​por defini-lo usando operadores de contrapressão.

BackpressureStrategy é semelhante aBackpressureMode presente na versão anterior do RxJava.

Existem cinco estratégias diferentes disponíveis no RxJava 2.

4.1. Amortecedor

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);
}

É semelhante a invocar o métodoonBackpressureBuffer() emFlowable,, mas não permite definir um tamanho de buffer ou a ação onOverflow explicitamente.

4.2. Drop

Podemos usarBackpressureStrategy.DROP para descartar os eventos que não podem ser consumidos em vez de armazená-los em buffer.

Novamente, isso é semelhante a usaronBackpressureDrop() emFlowable:

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. Mais recentes

Usar oBackpressureStrategy.LATEST forçará a fonte a manter apenas os eventos mais recentes, substituindo, assim, quaisquer valores anteriores se o consumidor não conseguir acompanhar:

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 parecem muito semelhantes quando olhamos para o código.

No entanto,BackpressureStrategy.LATEST substituirá os elementos que nosso assinante não pode controlar e manterá apenas os mais recentes, daí o nome.

BackpressureStrategy.DROP,, por outro lado, descartará os elementos que não podem ser manipulados. Isso significa que os elementos mais novos não serão necessariamente emitidos.

4.4. Erro

Quando estamos usandoBackpressureStrategy.ERROR,, estamos simplesmente dizendo quewe don’t expect backpressure to occur. Consequentemente, umMissingBackpressureException deve ser lançado se o consumidor não conseguir acompanhar a fonte:

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. Ausência de

Se usarmosBackpressureStrategy.MISSING, a fonte enviará os elementos sem descartar ou armazenar em buffer.

O downstream terá que lidar com estouros neste caso:

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);
}

Em nossos testes, estamos excluindoMissingbackpressureException para as estratégiasERROReMISSING. Como ambos irão lançar tal exceção quando o buffer interno da fonte estiver sobrecarregado.

No entanto, é importante notar que ambos têm uma finalidade diferente.

Devemos usar o primeiro quando não esperamos contrapressão de forma alguma, e queremos que a fonte lance uma exceção no caso de ocorrer.

Este último pode ser usado se não quisermos especificar um comportamento padrão na criação deFlowable. E vamos usar operadores de contrapressão para defini-lo mais tarde.

5. Sumário

Neste tutorial, apresentamos a nova classe introduzida em RxJava2 chamadaFlowable.

Para encontrar mais informações sobre o próprioFlowable e sua API, podemos consultar odocumentation.

Como sempre, todos os exemplos de código podem ser encontradosover on GitHub.