Очередь как тема в RxJava

Я ищу тему (или что-то подобное), которая может:

  1. можно получать предметы и держать их в очереди или буфере, если нет подписчиков
  2. как только у нас есть подписчик, все предметы потребляются и никогда не испускаются снова
  3. Я могу подписаться/отписаться На / от темы

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 ответов


Я достигаю ожидаемого результата, создавая настраиваемый наблюдаемый объект, который обертывает тему публикации и обрабатывает кэш эмиссии, если нет подключенных подписчиков. Проверить его.

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

надеюсь, что это помогает!

С Наилучшими Пожеланиями.


Если вы хотите дождаться только одного подписчика, используйте UnicastSubject но обратите внимание, что если вы отпишетесь в середине, все последующие элементы очереди будут потеряны.

Edit:

Как только мы имеем a абонент все элементы потребляются и никогда не испускаются снова

для нескольких абонентов, используйте ReplaySubject.


У меня была похожая проблема, мои требования были:

  • должен поддерживать воспроизведение значений, когда нет подписанного наблюдателя
  • должен позволить только одному наблюдателю подписаться одновременно
  • должен позволить другому наблюдателю подписаться, когда первый наблюдатель расположен

я реализовал его как RxRelay но реализация для темы будет аналогичной:

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

смотреть на это суть для больше