Android RxJava:同步/立即返回缓存值

Android RxJava:同步/立即返回缓存值,android,multithreading,rx-java,rx-android,Android,Multithreading,Rx Java,Rx Android,我想看看是否有一种方法可以从一个可能需要很长时间才能发出的可观察对象同步返回一个缓存值。当然,如果它需要执行io/计算,那么应该在计算线程上执行,但是如果它以前已经执行过,那么应该是同步的,并且避免在线程之间来回跳跃。下面是我的一些示例代码: public void bind(ItemViewHolder holder) { getCalculationObservable() .observeOn(AndroidSchedulers.mainThread())

我想看看是否有一种方法可以从一个可能需要很长时间才能发出的可观察对象同步返回一个缓存值。当然,如果它需要执行io/计算,那么应该在计算线程上执行,但是如果它以前已经执行过,那么应该是同步的,并且避免在线程之间来回跳跃。下面是我的一些示例代码:

public void bind(ItemViewHolder holder) {
    getCalculationObservable()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(value -> {
                    holder.showValue(value); // This will happen after bind has finished
                }
            });
}

public Observable<Integer> getCalculationObservable() {
    if (mObservable == null) {

        mObservable = Observable.fromCallable(this::calculate)
                .subscribeOn(Schedulers.computation())
                .cache();

    }
    return mObservable;
}

public int calculate() throws InterruptedException {
    Thread.sleep(1000);
    return mValue * 1000;
}

为了进一步说明这一点,如果在第二个onNext链上添加一个wait(),您将永远无法完成,因为它将在您阻塞的同一线程中排队的某个线程上等待。

更新:

first get: doOnSubscribe execution thread - main
callable: body execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnNext execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnNext execution thread - RxComputationThreadPool-1
first get: doOnNext execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnComplete execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnComplete execution thread - RxComputationThreadPool-1
first get: doOnComplete execution thread - RxComputationThreadPool-1
---------- first get executed ------------
second get: doOnSubscribe execution thread - main
cached Observable: after cache() - doOnNext execution thread - main
second get: doOnNext execution thread - main
cached Observable: after cache() - doOnComplete execution thread - main
second get: doOnComplete execution thread - main
使用
AndroidSchedulers.mainThread()
scheduler应用
observeOn
时,通过内部使用
postDelayed
将下游事件发布到
MessageQueue
。这就是为什么位于第二个可观察对象之后的代码在该可观察对象完成之前执行(或者如果我们使用
test().await()
),代码会冻结)。一种可能的解决方案是使用
主题
作为数据源和订户之间的代理。查看本文了解更多信息-

还有一些有用的文章:


解释为什么
缓存不切换线程:

first get: doOnSubscribe execution thread - main
callable: body execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnNext execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnNext execution thread - RxComputationThreadPool-1
first get: doOnNext execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnComplete execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnComplete execution thread - RxComputationThreadPool-1
first get: doOnComplete execution thread - RxComputationThreadPool-1
---------- first get executed ------------
second get: doOnSubscribe execution thread - main
cached Observable: after cache() - doOnNext execution thread - main
second get: doOnNext execution thread - main
cached Observable: after cache() - doOnComplete execution thread - main
second get: doOnComplete execution thread - main
您的
Observable
已经同步返回缓存值,因为
cache
不会为每个订阅者订阅整个上游(因此在您的情况下,它不会切换线程)。它只做一次,然后只记住项目的顺序。对于每个新订户,
cache
只需重放它


示例: (用科特林书写)

如您所见,当存在缓存值时,线程不会被切换


另外,我假设您使用RxJava2。

实际上,它没有。虽然第二个get确实出现在主线程上,但它不会同步返回。相反,它在循环器上排队,稍后执行。这可以通过第二次获取后的日志很容易地验证(当然,这里不能使用wait/test)。值得注意的是,一个很大的区别是,您的observable不执行observesOn,这似乎迫使进行排队。observesOn(主线程)基本上是一个需求,因为它不能关闭主线程和所有线程的视图。我故意省略了observeOn,以表明当存在缓存值时线程不会切换。我编辑了应答,添加了很多日志调用。还有,当有第二个get时,计算调度程序不会参与其中。@DavidLiu,你为什么这么认为?如果是这样的话,那么测试将在打印所有可观察到的日志之前结束。省略observeOn正是您没有遇到我所说的问题的原因。此外,线程切换不是我要说的问题。正如我所说,这是将消息排队到循环器上,并在下一次传递时执行它。为此,请使用
behavior subject
:将客户端订阅到subject,并在内部将可观察到的项目推送到该主题中。这就是如何在后台线程上执行和发射(主线程上的缓存/新值)。
//here is the same logic as yours
private var observable: Observable<Int>? = null
    get() {
        if(field==null)
            field = Observable.fromCallable {
                System.out.println("callable: execution thread - ${Thread.currentThread().name}")
                Thread.sleep(1000)
                return@fromCallable 1000
            }
                    .subscribeOn(Schedulers.computation())
                    .doOnNext     { System.out.println("cached Observable: before cache() - doOnNext execution thread - ${Thread.currentThread().name}") }
                    .doOnComplete { System.out.println("cached Observable: before cache() - doOnComplete execution thread - ${Thread.currentThread().name}") }
                    .cache()
                    .doOnNext     { System.out.println("cached Observable: after cache() - doOnNext execution thread - ${Thread.currentThread().name}") }
                    .doOnComplete { System.out.println("cached Observable: after cache() - doOnComplete execution thread - ${Thread.currentThread().name}") }

        return field
    }

@Test
fun test() {
    observable!!
            .doOnSubscribe { System.out.println("first get: doOnSubscribe execution thread - ${Thread.currentThread().name}") }
            .doOnNext      { System.out.println("first get: doOnNext execution thread - ${Thread.currentThread().name}") }
            .doOnComplete  { System.out.println("first get: doOnComplete execution thread - ${Thread.currentThread().name}") }
            .test()
            .await()

    System.out.println("---------- first get executed ------------")

    observable!!
            .doOnSubscribe { System.out.println("second get: doOnSubscribe execution thread - ${Thread.currentThread().name}") }
            .doOnNext      { System.out.println("second get: doOnNext execution thread - ${Thread.currentThread().name}") }
            .doOnComplete  { System.out.println("second get: doOnComplete execution thread - ${Thread.currentThread().name}") }
            .subscribe()
}
first get: doOnSubscribe execution thread - main
callable: body execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnNext execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnNext execution thread - RxComputationThreadPool-1
first get: doOnNext execution thread - RxComputationThreadPool-1
cached Observable: before cache() - doOnComplete execution thread - RxComputationThreadPool-1
cached Observable: after cache() - doOnComplete execution thread - RxComputationThreadPool-1
first get: doOnComplete execution thread - RxComputationThreadPool-1
---------- first get executed ------------
second get: doOnSubscribe execution thread - main
cached Observable: after cache() - doOnNext execution thread - main
second get: doOnNext execution thread - main
cached Observable: after cache() - doOnComplete execution thread - main
second get: doOnComplete execution thread - main