如何在RxJava中正确转换多播可观察对象

时间:2018-05-18 20:48:51

标签: java android rx-java reactive-programming rx-java2

假设我有一个事件发射数据源,我想将其转换为反应流。数据源由资源绑定(例如,定期发送更新状态的套接字),因此我希望共享该资源的单个订阅。使用带有replay的单个observable(对于新订户来立即获得当前值)和refCount运算符似乎非常适合。例如,他的MyDataProvider单身人员的样子如下:

private final Observable<MyData> myDataObservable = Observable.<MyData>create(emitter -> {
    // Open my resource here and emit data into observable
})
    .doOnDispose(() -> {
        // Close my resource here
    })
    .replay(1)
    .refCount();

public Observable<MyData> getMyDataObservable() {
    return myDataObservable;
}

但是,现在让我们说我有另一个数据源需要第一个数据源的结果来计算它自己的值:

private final Observable<AnotherData> anotherDataObservable = getMyDataProvider().getMyDataObservable()
    .flatMap(myData -> {
        // Call another data source and return the result here
    })

public Observable<AnotherData> getAnotherDataObservable() {
    return anotherDataObservable;
}

我的设置开始崩溃了。第一个observable的多播仅适用于refCount运算符。在那之后,一切都再次单播。这意味着如果对anotherDataProvider进行两次单独的订阅,flatMap运算符将被调用两次。我看到了两个解决方法,但我不喜欢这两个:

1。在多播发生之前转换第一个可观察的

简单的解决方法似乎是为我保存的单播变体myDataObservable某处,多播操作之前进行,然后执行在anotherDataObservable然而,如果这两个观测值都位于diferent模块多播操作,这种解决方法会使代码非常不优雅,需要MyDataProvider公开两个看似返回相同数据的不同observable。

2。只需使用重复的多播运营商

第二种解决方法似乎是在replay中再次应用这些refCountanotherDataObservable运算符。但这会造成效率低下,因为myDataObservable中的第一个多播运算符已经应用,但现在什么也没做,除了浪费内存和CPU周期。

这两种解决方法还涉及AnotherDataProviderMyDataProvider的耦合。如果将来MyDataProvider不再需要更改和多播,我还必须更新AnotherDataProvider以从那里删除多播运营商。

解决此问题的更优雅的方法是什么?我是否可以更好地避免这个问题?

3 个答案:

答案 0 :(得分:3)

关于您的第一种方法,在当前设置中,您的anotherDataObservable使用myDataObservable,据我所知,它们是逻辑耦合的,因为它们使用相同的来源。所以你需要为它们提供一些基本的共享逻辑。我将它提取到一个公共模块,它将公开observable的单播版本,然后使myDataObservableanotherDataObservable在每个添加多播逻辑的不同模块中使用它。

另一种选择是让一个类通过订阅来监控您的资源,就像在myDataObservable中一样,在onNext中进行处理并使用Subject发布映射结果,即BehavioralSubject,如果您希望始终可以访问上次发布的值,并将原始结果与另一个主题相关联。客户将订阅该主题,并将获得在监控类中仅计算一次的映射值或原始值。

P.S。在订阅之前,请记得向您的主题添加背压策略。

如果这些选项不适合您,请考虑避免多次调用flatMap是否真的很重要?您的代码非常简单,是一个重要的指标。如果flatMap不重,你可以让它多次运行。

答案 1 :(得分:1)

您可以使用&#34; publish()。refCount()&#34;串联以允许共享单个订户。 由于它们经常被使用,因此它们具有别名共享()。

您还可以使用ConnectableObservable。但是在使用重播时要小心 ConnectableObservables。

  

如果在将Obplay运算符转换为可连接的Observable之前将其应用于Observable,则生成的可连接Observable将始终向任何未来的观察者发出相同的完整序列,即使是在可连接的Observable开始发出之后订阅的观察者也是如此其他订阅观察者的项目。正如文件所述:

答案 2 :(得分:0)

您可以拆分单播和多播流,但这是多余的。我认为第二种方法更好,顺便说一句,replayrefcount运营商实际上做事情并不是浪费。

当您致电Observable启用多播时,您正在将myDataObservable ConenctableObservable转换为replay(1)。 然后在使用refcount()时在内部订阅它,这也为后续订阅提供单点;在此之后,所有人都再次单播。

您真正希望在anotherDataObservable中实现的目标是相同的,因此,与myDataObservable中的完全相同。