【问题标题】:RxJava cache last item for future subscribersRxJava 为未来的订阅者缓存最后一项
【发布时间】:2015-11-10 19:26:27
【问题描述】:

我已经实现了简单的 RxEventBus,它开始发出事件,即使没有订阅者。我想缓存最后发出的事件,这样如果第一个/下一个订阅者订阅,它只会收到一个(最后一个)项目。

我创建了描述我的问题的测试类:

public class RxBus {

ApplicationsRxEventBus applicationsRxEventBus;

public RxBus() {
    applicationsRxEventBus = new ApplicationsRxEventBus();
}

public static void main(String[] args) {
    RxBus rxBus = new RxBus();
    rxBus.start();
}

private void start() {
    ExecutorService executorService = Executors.newScheduledThreadPool(2);

    Runnable runnable0 = () -> {
        while (true) {
            long currentTime = System.currentTimeMillis();
            System.out.println("emiting: " + currentTime);
            applicationsRxEventBus.emit(new ApplicationsEvent(currentTime));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    Runnable runnable1 = () -> applicationsRxEventBus
            .getBus()
            .subscribe(new Subscriber<ApplicationsEvent>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable throwable) {

                }

                @Override
                public void onNext(ApplicationsEvent applicationsEvent) {
                    System.out.println("runnable 1: " + applicationsEvent.number);
                }
            });

    Runnable runnable2 = () -> applicationsRxEventBus
            .getBus()
            .subscribe(new Subscriber<ApplicationsEvent>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable throwable) {

                }

                @Override
                public void onNext(ApplicationsEvent applicationsEvent) {
                    System.out.println("runnable 2: " + applicationsEvent.number);
                }
            });


    executorService.execute(runnable0);
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    executorService.execute(runnable1);
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    executorService.execute(runnable2);
}

private class ApplicationsRxEventBus {
    private final Subject<ApplicationsEvent, ApplicationsEvent> mRxBus;
    private final Observable<ApplicationsEvent> mBusObservable;

    public ApplicationsRxEventBus() {
        mRxBus = new SerializedSubject<>(BehaviorSubject.<ApplicationsEvent>create());
        mBusObservable = mRxBus.cache();
    }

    public void emit(ApplicationsEvent event) {
        mRxBus.onNext(event);
    }

    public Observable<ApplicationsEvent> getBus() {
        return mBusObservable;
    }
}
private class ApplicationsEvent {
    long number;

    public ApplicationsEvent(long number) {
        this.number = number;
    }
}
}

即使没有订阅者,runnable0 也会发出事件。 runnable1 在 3 秒后订阅,并收到最后一项(这没关系)。但 runnable2 在 runnable1 后 3 秒后订阅,并接收 runnable1 收到的所有项目。我只需要收到 runnable2 的最后一项。我在 RxBus 中尝试过缓存事件:

private class ApplicationsRxEventBus {
    private final Subject<ApplicationsEvent, ApplicationsEvent> mRxBus;
    private final Observable<ApplicationsEvent> mBusObservable;

    private ApplicationsEvent event;

    public ApplicationsRxEventBus() {
        mRxBus = new SerializedSubject<>(BehaviorSubject.<ApplicationsEvent>create());
        mBusObservable = mRxBus;
    }

    public void emit(ApplicationsEvent event) {
        this.event = event;
        mRxBus.onNext(event);
    }

    public Observable<ApplicationsEvent> getBus() {
        return mBusObservable.doOnSubscribe(() -> emit(event));
    }
}

但问题是,当 runnable2 订阅时,runnable1 会收到两次事件:

emiting: 1447183225122
runnable 1: 1447183225122
runnable 1: 1447183225122
runnable 2: 1447183225122
emiting: 1447183225627
runnable 1: 1447183225627
runnable 2: 1447183225627

我敢肯定,有 RxJava 运算符可以做到这一点。如何做到这一点?

【问题讨论】:

    标签: caching rx-java event-bus


    【解决方案1】:

    您的ApplicationsRxEventBus 除了所有缓存的事件外,还通过在每次订阅时重新发送存储的事件来完成额外的工作。

    您只需要一个 BehaviorSubject + toSerialized,因为它会保留最后一个事件并自行将其重新发送给订阅者。

    【讨论】:

    • 这解决了我的问题。我还阅读了有关 'replaySubject' 和 'createWithSize(1)' 的信息,似乎两者都给出了相同的结果。
    【解决方案2】:

    您使用了错误的界面。当你订阅一个 cold Observable 时,你会得到它的所有事件。你需要先把它变成hot Observable。这是通过使用 publish 方法从您的 Observable 创建一个 ConnectableObservable 来完成的。然后您的观察者调用connect 开始接收事件。

    您还可以在本教程的Hot and Cold observables 部分了解更多信息。

    【讨论】:

    • 如果 Observable 不知道订阅者的数量和订阅时间怎么办。和情况,当 Observable 发出事件时,当没有订阅者时,然后有一个订阅者订阅。它需要接收最后一个事件。我希望将 RxBus 分开。我阅读了有关冷热可观察物的信息。我找到了重播主题。 @akarnokd BehaviourSubject 就能解决问题。
    • 我仍然会将总线设为 ConnectableObservable。为此,您可以连接一个大小为 1 的 ReplaySubject 来缓存最后一个事件,就像您说的那样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 2015-09-01
    • 1970-01-01
    相关资源
    最近更新 更多