RxJava用RxRelayの紹介

RxJavaのRxRelayの概要

1. 前書き

RxJavaの人気により、その機能を拡張する複数のサードパーティライブラリが作成されました。

これらのライブラリの多くは、開発者がRxJavaを使用する際に扱っていた典型的な問題への回答でした。 RxRelayはこれらのソリューションの1つです。

2. Subjectの処理

簡単に言えば、SubjectObservableObserver.の間のブリッジとして機能します。これはObserverであるため、1つ以上のObservablesにサブスクライブし、からイベントを受信できます。それら。

また、同時にObservableである場合、イベントを再送信したり、サブスクライバーに新しいイベントを発行したりできます。 Subjectの詳細については、このarticleを参照してください。

Subjectの問題の1つは、onComplete()またはonError()を受信した後、データを移動できなくなることです。 望ましい動作である場合もあれば、そうでない場合もあります。

このような動作が望ましくない場合は、RxRelayの使用を検討する必要があります。

3. Relay

Relayは基本的にSubjectですが、onComplete()onError(),を呼び出す機能がないため、常にデータを送信できます。

これにより、誤って端末の状態をトリガーすることを心配することなく、さまざまな種類のAPI間のブリッジを作成できます。

RxRelayを使用するには、プロジェクトに次の依存関係を追加する必要があります。


  com.jakewharton.rxrelay2
  rxrelay
  1.2.0

4. Relayの種類

ライブラリで使用できるRelayには3つの異なるタイプがあります。 ここでは、3つすべてについて簡単に説明します。

4.1. PublishRelay

このタイプのRelayは、Observerがサブスクライブすると、すべてのイベントを再送信します。

イベントはすべてのサブスクライバーに発行されます。

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

この場合、イベントのバッファリングはないため、この動作はコールドObservable.に似ています。

4.2. BehaviorRelay

このタイプのRelayは、Observerがサブスクライブすると、最後に観測されたイベントと後続のすべてのイベントを再送信します。

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

BehaviorRelayを作成するときに、他に発行するイベントがない場合に発行されるデフォルト値を指定できます。

デフォルト値を指定するには、createDefault()メソッドを使用できます。

public void whenObserverSubscribedToBehaviorRelay_itReceivesDefaultValue() {
    BehaviorRelay behaviorRelay = BehaviorRelay.createDefault(1);
    TestObserver firstObserver = new TestObserver<>();
    behaviorRelay.subscribe(firstObserver);
    firstObserver.assertValue(1);
}

デフォルト値を指定したくない場合は、create()メソッドを使用できます。

public void whenObserverSubscribedToBehaviorRelayWithoutDefaultValue_itIsEmpty() {
    BehaviorRelay behaviorRelay = BehaviorRelay.create();
    TestObserver firstObserver = new TestObserver<>();
    behaviorRelay.subscribe(firstObserver);
    firstObserver.assertEmpty();
}

4.3. ReplayRelay

このタイプのRelayは、受信したすべてのイベントをバッファリングしてから、サブスクライブしているすべてのサブスクライバーに再送信します。

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

すべての要素がバッファリングされ、すべてのサブスクライバーが同じイベントを受信するため、this behavior is similar to the cold Observable.

ReplayRelayを作成するときに、最大のバッファサイズとイベントの存続時間を提供できます。

限られたバッファサイズでRelayを作成するには、createWithSize()メソッドを使用できます。 バッファリングするイベントが設定したバッファサイズよりも多い場合、前の要素は破棄されます。

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

createWithTime()メソッドを使用して、バッファリングされたイベントに残す最大時間でReplayRelayを作成することもできます。

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

上記のすべての型は、共通の抽象クラスRelay,を拡張します。これにより、独自のカスタムRelayクラスを作成できます。

カスタムRelayを作成するには、accept(), hasObservers()subscribeActual().の3つのメソッドを実装する必要があります。

ランダムに選択されたサブスクライバーの1つにイベントを再送信する単純なリレーを作成しましょう。

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 observer) {
        observers.add(observer);
        observer.onSubscribe(Disposables.fromRunnable(
          () -> System.out.println("Disposed")));
    }
}

これで、1人のサブスクライバーのみがイベントを受信することをテストできます。

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. 結論

このチュートリアルでは、Subjectに似ていますが、端末状態をトリガーする機能がないRxRelayについて説明しました。

詳細については、documentationを参照してください。 そして、いつものように、すべてのコードサンプルはover on GitHubで見つけることができます。