我正在使用Reactive Extensions(RxJava 2)对蓝牙设备执行RPC调用,从而导致传入的数据流,我随后对其进行了解析,还使用了Rx。生成的API是简单的Flowable<DownloadedRecord>
。为此,我正在Rx API of the Sweetblue library for Android之上构建。
我的问题是,在“请求”设备开始流传输与及时订阅流以确保没有任何数据包丢失之间存在竞争状态。
我使用Completable
首先执行RPC调用以请求数据流开始andThen( readRecords )
。在readRecords
有时间订阅此流之前,Sweetblue发出了一些数据包,从而“中断” readRecords
。
要摆脱这种具体情况,请采用以下独立代码:
val numbers = PublishSubject.create<Int>()
var currentTotal = 0
val sumToTen = numbers
.doOnNext { currentTotal += it }
.doOnNext { println( "Produced $it" ) }
.takeUntil { currentTotal >= 10 }
.doOnComplete { println( "Produced a total of $currentTotal." ) }
Completable.fromAction { numbers.onNext( 9 ) } ) // Mimic race condition.
.andThen( sumToTen )
.subscribe { println( "Observed: $it, Current total: $currentTotal" ) }
numbers.onNext( 1 )
numbers.onNext( 9 )
调用模仿了竞争条件。 sumToTen
从未观察到此数字,因为sumToTen
仅在下一行订阅。因此,流永远不会完成。
经过一番调查,我了解我可以使用replay
和connect
'解决'这个问题。
val numbers = PublishSubject.create<Int>()
var currentTotal = 0
val sumToTen = numbers
.doOnNext { currentTotal += it }
.doOnNext { println( "Produced $it" ) }
.takeUntil { currentTotal >= 10 }
.doOnComplete { println( "Produced a total of $currentTotal." ) }
.replay( 1 ) // Always replay last item upon subscription.
Completable.fromAction { sumToTen.connect() }
.andThen( Completable.fromAction { numbers.onNext( 9 ) } )
.andThen( sumToTen )
.subscribe { println( "Observed: $it, Current total: $currentTotal" ) }
numbers.onNext( 1 )
现在sumToTen
流完成了,因为通过在“开始流数据”(sumToThen
)之前首先连接到onNext( 9 )
,该流订阅了numbers
,因此会发生预期的副作用(currentTotal
)。 但是,仅当replay
缓冲区足够大(在这种情况下为)时,才会观察到'9'。例如,将replay( 1 )
替换为publish
将使流完成(“总共产生10个”),但不会观察到“ 9”。
我对这种解决方案不完全满意,原因有两个:
replay
缓冲区的大小是任意的。replay
中指定数量的元素保留在内存中,即使目的是直到订阅为止。实际上,这两个都不是真正的问题,但是从可维护性的角度来看这是一个大问题:代码没有清楚地传达意图。
是否有更好的方法来应对这种情况?例如:
replay
运算符,只为一个订户重播(因此,一旦第一次发出,就删除缓存)。publish/connect
上探索的方法完全不同吗?