Introdução ao RxRelay for RxJava
1. Introdução
A popularidade do RxJava levou à criação de várias bibliotecas de terceiros que ampliam sua funcionalidade.
Muitas dessas bibliotecas eram uma resposta para problemas típicos com os quais os desenvolvedores estavam lidando ao usar o RxJava. RxRelay é uma dessas soluções.
2. Lidando com umSubject
Simplificando, aSubject atua como uma ponte entreObservableeObserver. Uma vez que é umObserver, ele pode se inscrever em um ou maisObservablese receber eventos de eles.
Além disso, dado que é ao mesmo tempo umObservable, ele pode reemitir eventos ou emitir novos eventos para seus assinantes. Mais informações sobreSubject podem ser encontradas nestearticle.
Um dos problemas comSubject é que depois de receberonComplete() ouonError() - não é mais capaz de mover dados. Às vezes é o comportamento desejado, mas às vezes não é.
Nos casos em que tal comportamento não é desejado, devemos considerar o uso deRxRelay.
3. Relay
UmRelay é basicamente umSubject, mas sem a capacidade de chamaronComplete()eonError(),, portanto, é constantemente capaz de emitir dados.
Isso nos permite criar pontes entre diferentes tipos de API sem se preocupar em acionar acidentalmente o estado do terminal.
Para usarRxRelay, precisamos adicionar a seguinte dependência ao nosso projeto:
com.jakewharton.rxrelay2
rxrelay
1.2.0
4. Tipos deRelay
Existem três tipos diferentes deRelay disponíveis na biblioteca. Vamos explorar rapidamente todos os três aqui.
4.1. PublishRelay
Este tipo deRelay reemitirá todos os eventos uma vez queObserver tenha se inscrito nele.
Os eventos serão emitidos para todos os assinantes:
public void whenObserverSubscribedToPublishRelay_itReceivesEmittedEvents() {
PublishRelay publishRelay = PublishRelay.create();
TestObserver firstObserver = TestObserver.create();
TestObserver secondObserver = TestObserver.create();
publishRelay.subscribe(firstObserver);
firstObserver.assertSubscribed();
publishRelay.accept(5);
publishRelay.accept(10);
publishRelay.subscribe(secondObserver);
secondObserver.assertSubscribed();
publishRelay.accept(15);
firstObserver.assertValues(5, 10, 15);
// second receives only the last event
secondObserver.assertValue(15);
}
Não há buffer de eventos neste caso, então este comportamento é semelhante a umObservable. frio
4.2. BehaviorRelay
Este tipo deRelay irá reemitir o evento mais recente observado e todos os eventos subsequentes, uma vez que Observer se inscreveu:
public void whenObserverSubscribedToBehaviorRelay_itReceivesEmittedEvents() {
BehaviorRelay behaviorRelay = BehaviorRelay.create();
TestObserver firstObserver = TestObserver.create();
TestObserver secondObserver = TestObserver.create();
behaviorRelay.accept(5);
behaviorRelay.subscribe(firstObserver);
behaviorRelay.accept(10);
behaviorRelay.subscribe(secondObserver);
behaviorRelay.accept(15);
firstObserver.assertValues(5, 10, 15);
secondObserver.assertValues(10, 15);
}
Quando estamos criandoBehaviorRelay, podemos especificar o valor padrão, que será emitido, se não houver outros eventos a serem emitidos.
Para especificar o valor padrão, podemos usar o métodocreateDefault():
public void whenObserverSubscribedToBehaviorRelay_itReceivesDefaultValue() {
BehaviorRelay behaviorRelay = BehaviorRelay.createDefault(1);
TestObserver firstObserver = new TestObserver<>();
behaviorRelay.subscribe(firstObserver);
firstObserver.assertValue(1);
}
Se não quisermos especificar o valor padrão, podemos usar o métodocreate():
public void whenObserverSubscribedToBehaviorRelayWithoutDefaultValue_itIsEmpty() {
BehaviorRelay behaviorRelay = BehaviorRelay.create();
TestObserver firstObserver = new TestObserver<>();
behaviorRelay.subscribe(firstObserver);
firstObserver.assertEmpty();
}
4.3. ReplayRelay
Este tipo deRelay armazena em buffer todos os eventos que recebeu e, em seguida, os reemite para todos os assinantes que o assinam:
public void whenObserverSubscribedToReplayRelay_itReceivesEmittedEvents() {
ReplayRelay replayRelay = ReplayRelay.create();
TestObserver firstObserver = TestObserver.create();
TestObserver secondObserver = TestObserver.create();
replayRelay.subscribe(firstObserver);
replayRelay.accept(5);
replayRelay.accept(10);
replayRelay.accept(15);
replayRelay.subscribe(secondObserver);
firstObserver.assertValues(5, 10, 15);
secondObserver.assertValues(5, 10, 15);
}
Todos os elementos são armazenados em buffer e todos os assinantes receberão os mesmos eventos, entãothis behavior is similar to the cold Observable.
Quando estamos criando oReplayRelay, podemos fornecer o tamanho máximo do buffer e tempo de vida para eventos.
Para criarRelay com tamanho de buffer limitado, podemos usar o métodocreateWithSize(). Quando houver mais eventos a serem armazenados em buffer do que o tamanho do buffer definido, os elementos anteriores serão descartados:
public void whenObserverSubscribedToReplayRelayWithLimitedSize_itReceivesEmittedEvents() {
ReplayRelay replayRelay = ReplayRelay.createWithSize(2);
TestObserver firstObserver = TestObserver.create();
replayRelay.accept(5);
replayRelay.accept(10);
replayRelay.accept(15);
replayRelay.accept(20);
replayRelay.subscribe(firstObserver);
firstObserver.assertValues(15, 20);
}
Também podemos criarReplayRelay com tempo máximo de saída para eventos em buffer usando o métodocreateWithTime():
public void whenObserverSubscribedToReplayRelayWithMaxAge_thenItReceivesEmittedEvents() {
SingleScheduler scheduler = new SingleScheduler();
ReplayRelay replayRelay =
ReplayRelay.createWithTime(2000, TimeUnit.MILLISECONDS, scheduler);
long current = scheduler.now(TimeUnit.MILLISECONDS);
TestObserver firstObserver = TestObserver.create();
replayRelay.accept(5);
replayRelay.accept(10);
replayRelay.accept(15);
replayRelay.accept(20);
Thread.sleep(3000);
replayRelay.subscribe(firstObserver);
firstObserver.assertEmpty();
}
5. Relay personalizado
Todos os tipos descritos acima estendem a classe abstrata comumRelay,, o que nos dá a capacidade de escrever nossa própria classeRelay personalizada.
Para criar umRelay personalizado, precisamos implementar três métodos:accept(), hasObservers()esubscribeActual().
Vamos escrever um relé simples que reemitirá o evento para um dos assinantes escolhidos aleatoriamente:
public class RandomRelay extends Relay {
Random random = new Random();
List> observers = new ArrayList<>();
@Override
public void accept(Integer integer) {
int observerIndex = random.nextInt() % observers.size();
observers.get(observerIndex).onNext(integer);
}
@Override
public boolean hasObservers() {
return observers.isEmpty();
}
@Override
protected void subscribeActual(Observer super Integer> observer) {
observers.add(observer);
observer.onSubscribe(Disposables.fromRunnable(
() -> System.out.println("Disposed")));
}
}
Agora podemos testar se apenas um assinante receberá o evento:
public void whenTwoObserversSubscribedToRandomRelay_thenOnlyOneReceivesEvent() {
RandomRelay randomRelay = new RandomRelay();
TestObserver firstObserver = TestObserver.create();
TestObserver secondObserver = TestObserver.create();
randomRelay.subscribe(firstObserver);
randomRelay.subscribe(secondObserver);
randomRelay.accept(5);
if(firstObserver.values().isEmpty()) {
secondObserver.assertValue(5);
} else {
firstObserver.assertValue(5);
secondObserver.assertEmpty();
}
}
6. Conclusão
Neste tutorial, demos uma olhada no RxRelay, um tipo semelhante aSubject, mas sem a capacidade de acionar o estado terminal.
Mais informações podem ser encontradas emdocumentation. E, como sempre, todos os exemplos de código podem ser encontradosover on GitHub.