像RxJava中的Subject一样排队

时间:2016-06-09 14:17:55

标签: rx-java

我正在寻找可以的主题(或类似的东西):

  1. 如果没有订阅者,则可以接收项目并将其保留在队列或缓冲区中
  2. 一旦我们有一个订阅者,所有项目都被消费,再也不会再被发送
  3. 我可以订阅/取消订阅主题
  4. BehaviorSubject几乎可以完成这项工作,但它保留了最后观察到的项目。

    更新

    根据接受的答案,我为单个观察项目制定了类似的解决方案。还添加了取消订阅部分以避免内存泄漏。

    class LastEventObservable private constructor(
            private val onSubscribe: OnSubscribe<Any>,
            private val state: State
    ) : Observable<Any>(onSubscribe) {
    
        fun emit(value: Any) {
            if (state.subscriber.hasObservers()) {
                state.subscriber.onNext(value)
            } else {
                state.lastItem = value
            }
        }
    
        companion object {
            fun create(): LastEventObservable {
                val state = State()
    
                val onSubscribe = OnSubscribe<Any> { subscriber ->
                    just(state.lastItem)
                            .filter { it != null }
                            .doOnNext { subscriber.onNext(it) }
                            .doOnCompleted { state.lastItem = null }
                            .subscribe()
    
                    val subscription = state.subscriber.subscribe(subscriber)
    
                    subscriber.add(Subscriptions.create { subscription.unsubscribe() })
                }
    
                return LastEventObservable(onSubscribe, state)
            }
        }
    
        private class State {
            var lastItem: Any? = null
            val subscriber = PublishSubject.create<Any>()
        }
    }
    

3 个答案:

答案 0 :(得分:11)

我实现了预期的结果,创建了一个自定义的Observable,它包装了一个发布主题,并在没有附加订阅者的情况下处理发射缓存。看看吧。

public class ExampleUnitTest {
    @Test
    public void testSample() throws Exception {
        MyCustomObservable myCustomObservable = new MyCustomObservable();

        myCustomObservable.emit("1");
        myCustomObservable.emit("2");
        myCustomObservable.emit("3");

        Subscription subscription = myCustomObservable.subscribe(System.out::println);

        myCustomObservable.emit("4");
        myCustomObservable.emit("5");

        subscription.unsubscribe();

        myCustomObservable.emit("6");
        myCustomObservable.emit("7");
        myCustomObservable.emit("8");

        myCustomObservable.subscribe(System.out::println);
    }
}

class MyCustomObservable extends Observable<String> {
    private static PublishSubject<String> publishSubject = PublishSubject.create();
    private static List<String> valuesCache = new ArrayList<>();

    protected MyCustomObservable() {
        super(subscriber -> {
            Observable.from(valuesCache)
                    .doOnNext(subscriber::onNext)
                    .doOnCompleted(valuesCache::clear)
                    .subscribe();

            publishSubject.subscribe(subscriber);
        });
    }

    public void emit(String value) {
        if (publishSubject.hasObservers()) {
            publishSubject.onNext(value);
        } else {
            valuesCache.add(value);
        }
    }
}

希望它有所帮助!

最诚挚的问候。

答案 1 :(得分:3)

如果您只想等待单个订阅者,请使用UnicastSubject,但请注意,如果您在中间取消订阅,则所有后续排队的项目都将丢失。

编辑:

  

一旦我们有 订阅者,所有项目都会消耗掉,而且不会再次发送

对于多个订阅者,请使用ReplaySubject

答案 2 :(得分:2)

我有类似的问题,我的要求是:

  • 当没有Observer订阅
  • 时,应支持重播值
  • 一次只允许一个Observer订阅
  • 当第一个观察者被处置时,应该允许另一个观察者订阅

我已将其实现为RxRelay,但Subject的实现方式类似:

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

请查看此Gist for more