使用缓冲区合并Hot和Cold Observable

时间:2018-02-21 02:27:20

标签: java android kotlin rx-java2 reactive-streams

我有2个数据来源。

  1. 服务调用,它获取项目列表的当前状态
  2. 这些项目的更新流
  3. 我正在尝试合并它们,以便在拨打服务电话时我不会丢失任何更新。

    我做了一个简单的测试来试验我认为显示我想要的东西

    private val coldObservable = Observable.just(1, 2, 3, 4, 5)
    private val subject = PublishSubject.create<Int>()
    private val hotObservable = subject.hide()
    
    @Test
    fun test() {
        subject.onNext(10)
    
        val test = Flowable.concat(
                coldObservable.toFlowable(BackpressureStrategy.BUFFER), hotObservable.toFlowable(BackpressureStrategy.BUFFER)
        )
                .doOnSubscribe { subject.onNext(20) }
                .test()
    
        test.await(1, TimeUnit.SECONDS)
        test.assertNotComplete()
        test.assertNotTerminated()
        subject.onComplete()
        test.assertComplete()
        test.assertValues(1,2,3,4,5)
    }
    

    按原样,此测试通过。但我真正想要的是

    test.assertValues(1,2,3,4,5,10,20)
    

    我认为背压很容易让我坚持排放,但我想它并不是因为它没有订阅。

    是否无法将这两个来源合并为一个流?

2 个答案:

答案 0 :(得分:0)

这是一个与我正在寻找的效果类似的解决方案。 它存储实时数据,然后在服务调用完成后对其进行处理

@Test
fun test2() {
    val randomString = "abc123"
    val realLog = ConcurrentHashMap<String, Boolean>()

    var isLive = false

    val subject = PublishSubject.create<Pair<String, Boolean>>()

    val subscription = subject.hide()
            .doOnNext {
                if (isLive) {
                    realLog[it.first] = it.second
                } else {

                    realLog[it.first+ randomString] = it.second
                }
            }.subscribe()

    subject.onNext(Pair("one", false))

    val coldTest = Observable.just("one","two","three")
            .map { Pair(it, true) }
            .doOnSubscribe {
                subject.onNext(Pair("twenty", false))
            }
            .doOnNext{
                realLog[it.first] = it.second
            }
            .doOnComplete {
                val iterator = realLog.keys.iterator()
                while(iterator.hasNext()){
                    val oldKey = iterator.next()
                    if(oldKey.contains(randomString)){
                        val newKey = oldKey.removeSuffix(randomString)
                        realLog[newKey] = realLog[oldKey]!!
                        realLog.remove(oldKey)
                    }
                }
                isLive = true
                subject.onNext(Pair("Thirty", false))
            }
            .test()

    coldTest.awaitTerminalEvent()
    coldTest.assertComplete()

    Assert.assertFalse(realLog["Thirty"]!!)

它不会发出单个流,我仍然不确定这是否可行。

答案 1 :(得分:0)

我不确定我是否理解您的所有代码(rx本人还不熟悉)。但是您的用例似乎与我的用例相同。

我的用例是这样(因此您可以确定它是否相关):我使用HTTP请求来获取数据结构的初始状态。但是Web套接字流一直在发送对该数据的更新。如果我首先初始化数据结构,然后使用流对其进行更新,则更新将在两者之间的短时间内丢失。因此,我需要先获取流,在等待HTTP请求返回数据结构的同时缓存其更新数据,然后将来自数据流(在缓存中)的更新积压应用于数据。随着套接字数据的传入,从那里进行更新。

我不认识Kotlin,所以在Java中:

public static void main(String[] args) throws InterruptedException {
    //This one would correspond to the continuous stream of updates.
    //It sends Long, one each second
    Observable<Long> listUpdater = Observable.interval(1, TimeUnit.SECONDS);
    //Acts as both observable and observer, that replays everything to its own
    //observers.
    Subject<Long> replaySubjbect = ReplaySubject.create();
    //Start getting items from listUpdater immediately
    listUpdater.subscribe(replaySubjbect);
    //To show that listUpdater moves along regardless of what you are doing, as would
    //be the case for example with a websocket.
    listUpdater.subscribe(i -> System.out.println("I am also watching, i=" + i));
    //This callable corresponds to whatever returns the initial state of the list,
    //for example some http request
    Callable<List<Long>> clbl = () -> Arrays.asList(1L, 2L, 3L, 4L, 5L);
    //This is where the merging takes place, although we are not using one of the merging
    //methods, but scanWith. 
    Observable<List<Long>> theStream = replaySubjbect.scanWith(clbl, (List<Long> list, Long l) -> {
        //This updater adds l to each element of the list
        System.out.println("I am adding " + l + " to each element in the list");
        return list.stream().mapToLong(k -> k + l).boxed().collect(Collectors.toList());            
    });
    //Simulate a response time for the http request, or whatever it is the callable is doing
    Thread.sleep(3000);
    //Now we get the stream of lists, updated for each of the update data sent by the
    //original listUpdater
    theStream.subscribe(list -> System.out.println("theStream sent me this: " + list));
    //Just to see how it works, we sleep some more
    Thread.sleep(5000); 
}

这将输出:

I am also watching, i=0
I am also watching, i=1
I am also watching, i=2
theStream sent me this: [1, 2, 3, 4, 5] <- cahce retrieval starts
I am adding 0 to each element in the list 
theStream sent me this: [1, 2, 3, 4, 5] 
I am adding 1 to each element in the list 
theStream sent me this: [2, 3, 4, 5, 6] 
I am adding 2 to each element in the list 
theStream sent me this: [4, 5, 6, 7, 8]
I am also watching, i=3     
I am adding 3 to each element in the list <- from here on, update list as updates come in
theStream sent me this: [7, 8, 9, 10, 11]
I am also watching, i=4
I am adding 4 to each element in the list
theStream sent me this: [11, 12, 13, 14, 15]
I am also watching, i=5
I am adding 5 to each element in the list
theStream sent me this: [16, 17, 18, 19, 20]
I am also watching, i=6
I am adding 6 to each element in the list
theStream sent me this: [22, 23, 24, 25, 26]
I am also watching, i=7
I am adding 7 to each element in the list
theStream sent me this: [29, 30, 31, 32, 33]

scanWith: http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#scanWith-java.util.concurrent.Callable-io.reactivex.functions.BiFunction-

您还可以使用扫描(直接提供初始数据,而不是使用Callable): http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#scan-R-io.reactivex.functions.BiFunction-