Rxjava-在订阅之前在PublishSubject上发出

时间:2019-04-30 17:16:18

标签: rx-java rx-java2

请考虑一种情况,在这种情况下,我们有一个流在发射字符串,并且希望将字符串保存在文件中。

我正在使用PublishSubject,它可以正常工作:

Subject<String> stream = PublishSubject.create();
stream.subscribe(str -> saveFile(str));
mReverseGeocoderStream.onNext("some-string1")
mReverseGeocoderStream.onNext("some-string2")

但是,这不起作用(仅发送some-string2

Subject<String> stream = PublishSubject.create();
mReverseGeocoderStream.onNext("some-string1")
stream.subscribe(str -> saveFile(str));
mReverseGeocoderStream.onNext("some-string2")

是否有办法使第二种情况也起作用?

也就是说,我们可以更改PublishSubject来确保它缓冲事件直到订阅者使用它们吗?

请注意,BehaviorSubject不是一个选项,因为重新订阅会导致另一个文件保存。它没有“消费事件”的概念。

我发现UnicastSubject几乎是我想要的,除了,当我取消订阅并随后重新订阅其他订阅者时,它失败,并出现IllegalStateException。


用例:

假设我们有一个android应用。它发出网络请求,在网络请求的后面,需要显示一个对话框。在发出请求时,用户将使应用程序后台运行。此时,我们将取消订阅正在监听信号以显示对话框的观察者。

网络请求返回,并发出信号显示对话框已触发到流。目前尚无人在听。用户将应用程序前台化。新订户将附加到网络请求管理器(ViewModel)。在这一点上,我希望将“未消耗”的信号传递给订户。

注意:我不能使用行为主题。如果执行此操作,则每次用户在后台运行应用程序时,都会显示该对话框。我希望事件在显示对话框后被消耗并结束。

1 个答案:

答案 0 :(得分:0)

进行了更多研究,发现了这一点:

https://gist.github.com/xsveda/8c556516079fde97d04b4b7e14a18463

来自:Queue like Subject in RxJava

请注意,它使用中继,但是如果您不想引入其他依赖项,则可以轻松地用主题替换中继。

我对其他解决方案持开放态度,也许会批评为什么这种解决方案不是很好的解决方案。

/**
 * Relay that buffers values when no Observer subscribed and replays them to Observer as requested. Such values are not replayed
 * to any other Observer.
 * <p>
 * This relay holds an unbounded internal buffer.
 * <p>
 * This relay allows only a single Observer at a time to be subscribed to it.
 * <p>
 * If more than one Observer attempts to subscribe to this Relay at the same time, they
 * will receive an IllegalStateException.
 *
 * @param <T> the value type received and emitted by this Relay subclass
 */
public final class CacheRelay<T> extends Relay<T> {

    private final ConcurrentLinkedQueue<T> queue = new ConcurrentLinkedQueue<>();
    private final PublishRelay<T> relay = PublishRelay.create();

    private CacheRelay() {
    }

    public static <T> CacheRelay<T> create() {
        return new CacheRelay<>();
    }

    @Override
    public void accept(T value) {
        if (relay.hasObservers()) {
            relay.accept(value);
        } else {
            queue.add(value);
        }
    }

    @Override
    public boolean hasObservers() {
        return relay.hasObservers();
    }

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        if (hasObservers()) {
            EmptyDisposable.error(new IllegalStateException("Only a single observer at a time allowed."), observer);
        } else {
            for (T element; (element = queue.poll()) != null; ) {
                observer.onNext(element);
            }
            relay.subscribeActual(observer);
        }
    }
}