单独重播合并的流

时间:2017-10-20 10:34:24

标签: rx-java rx-java2

我有一个ObservableTransformer由几个合并在一起的子流组成,如下例所示。

ObservableTransformer<Action, Result> actionsToResults =
        actions -> actions.publish(shared ->
                Observable.merge(
                        Observable.merge(
                                shared.ofType(SendMessageAction.class).compose(sendMessageTransformer),
                                shared.ofType(CheckMessageInputAction.class).compose(checkIfEmptyTransformer)),
                        model.messagesObservable
                                .observeOn(AndroidSchedulers.mainThread())
                )).replay(1).autoConnect();

//      ╔════╗
//   ╔══╣    ╠══╗
// ══╣  ╚════╝  ╠══(replay)>
//   ╚══════════╝

我正在重放最后发出的元素,但我想要的是单独重放流。像这样:

//      ╔════╗
//   ╔══╣    ╠═(replay)═╗
// ══╣  ╚════╝          ╠══>
//   ╚═════════(replay)═╝

因此,当我订阅整个流时,它会发出这两个内部流的最后一个元素。可以这样做吗?

编辑:为了使我的问题更清楚,我将扩展我认为问题所在。

我希望整个流是一个可连接的可观察对象。这很重要,因为它存在于无头片段内,并且不应该在旋转时重新触发所有调用。同时我想单独重播这些流。在外部合并中移动.replay(1).autoConnect()将不起作用。将在每个新订阅时重新创建整个流。在外部合并中添加自定义运算符,如:

private ObservableTransformer<Action, Result> actionsToResults =
            actions -> actions.publish(shared -> Observable.merge(Observable.merge(
                    shared.ofType(SendMessageAction.class).compose(sendMessageTransformer),
                    shared.ofType(CheckMessageInputAction.class).compose(checkIfEmptyTransformer)).lift(new ReplayLastOperator<Result>())
                    model.messagesObservable.lift(new ReplayLastOperator<>()).observeOn(AndroidSchedulers.mainThread())
            ));

ReplayLastOperator<Result>()重放每个新订阅的最后一个元素相当于移动replay(1).autoConnect(),在每个新订阅时重新创建整个流。所以没有什么可以重播的。最后一个.publish().autoConnect()的解决方案只会订阅内部流一次,因此我无法检测到上游的新订阅,因此重放也不起作用。

我已经没有想法了。

2 个答案:

答案 0 :(得分:0)

是。
您可以创建一个自定义运算符来缓存它看到的最后一项。

然后,当从上游收到onComplete时,它会重新发出它在发出onComplete之前看到的最后一项。该运算符的通用形式如下所示:

public class ReplayLast <T>  implements ObservableOperator<T, T> {
@Override
public Observer<? super T> apply(@NonNull final Observer<? super T> observer) throws Exception {
    return new Observer<T>(){

        Disposable disposable;
        T lastItem;  // cache for last item

        @Override
        public void onSubscribe(@NonNull Disposable d) {
            disposable = d;
            observer.onSubscribe(d);
        }

        @Override
        public void onNext(@NonNull T t) {
            lastItem = t;  // cache each item from up stream
            observer.onNext(t); // emit as usual
        }

        @Override
        public void onError(@NonNull Throwable e) {
            observer.onError(e);
        }

        @Override
        public void onComplete() {
            if(lastItem != null){
                // use the cache to emit the item again before signaling the complete downstream
                observer.onNext(lastItem);
            }
            observer.onComplete();
        }
    };
}

然后只是在适当的地方使用它。应该类似于以下内容:

ObservableTransformer<Action, Result> actionsToResults =
        actions -> actions.publish(shared ->
                Observable.merge(
                        Observable.merge(
                                shared.ofType(SendMessageAction.class).compose(sendMessageTransformer),
                                shared.ofType(CheckMessageInputAction.class).compose(checkIfEmptyTransformer)).lift(new ReplayLast()),
                        model.messagesObservable.lift(new ReplayLast())
                                .observeOn(AndroidSchedulers.mainThread())
                )).autoConnect();

答案 1 :(得分:0)

对于我的具体案例,我解决了这个问题。我将添加一些额外的信息,以便您了解它是否适​​用于您的情况。

在我的例子中,我想要重放的所有流都返回一个不同类型的对象,由一个抽象类密封。所以我对这个问题的解决方案是实现我自己的ObservableTransoformer,名为ReplayLastByClassType,它在地图中兑现按类型接收的最后一个元素。您只需致电.compose(ReplayLastByClassType.<SealingClass>instance()或仅ReplayLastByClassType.instance()即可申请。

变换器在订阅时重新发出兑现值。之后,它会继续传递连续onNext次呼叫的元素。

  • 警告1. onSubscribe中返回的元素顺序不一定保留。(在我的情况下这不是必需的)。
  • 警告2.即使转换后的观察者的所有订阅者都取消订阅,上游观察者也不会取消订阅!!! 这是设计使然。

即使您要重播的流返回相同类型的对象,此方法也可能对您有用。您必须将结果包装到单独的帮助程序类中以强制按类分离。

最后是定制的ObservableTransformer,快乐的复制粘贴:)

public final class ReplayLastByClassType<T> implements ObservableTransformer<T, T> {

    private static final ReplayLastByClassType<Object> INSTANCE = new ReplayLastByClassType<>();

    /** The singleton instance of this transformer **/
    @SuppressWarnings("unchecked")
    public static <T> ReplayLastByClassType<T> instance() {
        return (ReplayLastByClassType<T>) INSTANCE;
    }

    private ReplayLastByClassType() {
    }

    @Override
    public Observable<T> apply(Observable<T> upstream) {
        LastSeen<T> lastSeen = new LastSeen<>();
        return new LastSeenObservable<>(upstream.doOnNext(lastSeen).publish().autoConnect(), lastSeen);
    }

    static final class LastSeen<T> implements Consumer<T> {
        ConcurrentHashMap<Class, T> values = new ConcurrentHashMap<>();

        @Override public void accept(T latest) {
            values.put(latest.getClass(), latest);
        }
    }

    static final class LastSeenObservable<T> extends Observable<T> {
        private final Observable<T> upstream;
        private final LastSeen<T> lastSeen;

        LastSeenObservable(Observable<T> upstream, LastSeen<T> lastSeen) {
            this.upstream = upstream;
            this.lastSeen = lastSeen;
        }

        @Override
        protected void subscribeActual(Observer<? super T> observer) {
            upstream.subscribe(new LastSeenObserver<T>(observer, lastSeen));
        }
    }

    static final class LastSeenObserver<T> implements Observer<T> {
        private final Observer<? super T> downstream;
        private final LastSeen<T> lastSeen;

        LastSeenObserver(Observer<? super T> downstream, LastSeen<T> lastSeen) {
            this.downstream = downstream;
            this.lastSeen = lastSeen;
        }

        @Override public void onSubscribe(Disposable d) {
            downstream.onSubscribe(d);

            Map<Class, T> values = lastSeen.values;
            for (T t: values.values()) {
                downstream.onNext(t);
            }
        }

        @Override public void onNext(T value) {
            downstream.onNext(value);
        }

        @Override public void onComplete() {
            downstream.onComplete();
        }

        @Override public void onError(Throwable e) {
            downstream.onError(e);
        }
    }
}

PS:如果您在代码中看到任何错误,请在评论中纠正我。