Rxandroid SubscribeOn和ObserveOn之间的区别是什么

时间:2017-07-08 09:41:03

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

我只是在学习Rx-java和Rxandroid2,我只是觉得在SubscribeOn和ObserveOn之间的主要区别是什么。

6 个答案:

答案 0 :(得分:35)

SubscribeOn指定Observable将在其上运行的Scheduler。 ObserveOn指定观察者将观察此Observable的调度程序。

所以基本上,SubscribeOn主要是在后台线程上订阅(执行)(你不想在等待observable时阻塞UI线程),而且在ObserveOn中你想要在主线程上观察结果... < / p>

如果您熟悉AsyncTask,那么SubscribeOn类似于doInBackground方法,而ObserveOn类似于onPostExecute ......

答案 1 :(得分:18)

observeOn()只需更改下游所有运营商的主题。人们通常会有误解 observeOn也充当上游,但事实并非如此。

以下示例将更好地解释..

Observable.just("Some string")                  // UI
       .map(str -> str.length())               // UI
       .observeOn(Schedulers.computation())   // Changing the thread
       .map(length -> 2 * length)            // Computation
       .subscribe(---)

subscribeOn()只有影响当Observable将要订阅时将使用的线程,它将保留在下游。

Observable.just("Some String")              // Computation
  .map(str -> str.length())                // Computation
  .map(length -> 2 * length)              // Computation
  .subscribeOn(Schedulers.computation()) // -- changing the thread
  .subscribe(number -> Log.d("", "Number " + number));// Computation
  

排名无关紧要(subscribeOn()

为什么呢? 因为仅影响订阅时间。

  

遵守subscribeOn

联系人的方法

- &GT;基本示例:Observable.create

create正文中指定的所有工作都将在subscribeOn中指定的主题上运行。

另一个例子:Observable.justObservable.fromObservable.range

注意:所有这些方法都接受值,因此不要使用阻塞方法来创建这些值,因为subscribeOn不会影响它。

如果要使用阻止功能,请使用

<强> Observable.defer(() -> Obervable.just(blockingMenthod())));

  

重要事实:

subscribeOn不适用于Subjects

  

多个subscribeOn

如果流中有多个subscribeOn个实例,则只有第一个具有实际效果。

  

订阅&amp; subscribeOn

人们认为subscribeOnObservable.subscribe有关,但与它没有什么特别之处。  仅影响订阅阶段

<强> TL;博士 如果以上都没有任何意义, 看看这段代码片段

 Observable.just("Some string")                 
           .map(str -> str.length())              
           .observeOn(Schedulers.computation())   
           .map(length -> 2 * length)   
           .observeOn(AndroidSchedulers.mainThread())
           .subscribeOn(Schedulers.io())
           .subscribe(---)
  

观察一个可观察的,在 UI 线程上执行地图功能,现在切换到计算线程并立即执行map(length -> 2 * length)功能确保您观察到输出线程,但执行 IO 主题中subscribe()下定义的所有任务。

资料来源:TomekPolański(Medium

答案 2 :(得分:5)

摘要

  • 使用observeOn设置线程用于回调“进一步向下(在其下方)”,例如doOnNextmap中的代码块。 / li>
  • 使用subscribeOn设置线程用于初始化“上游(在其上方)”,例如doOnSubscribeObservable.justObservable.create。 / li>
  • 两个方法都可以多次调用,每个调用都覆盖以前的方法。 位置很重要。

让我们以一个示例来遍历此主题:我们想找到字符串“ user1032613”的长度。对于计算机而言,这不是一件容易的事,因此很自然地,我们会在后台线程中执行大量计算,以避免冻结应用程序。

observeOn

我们可以根据需要多次调用observeOn,它可以控制其下所有回调的线程。它易于使用,并且可以按照您的期望工作。

例如,我们将在主UI线程上显示进度条,然后在另一个线程中执行密集/阻止操作,然后返回主UI线程以更新结果:

    Observable.just("user1032613")

            .observeOn(mainThread) // set thread for operation 1
            .doOnNext {
                /* operation 1 */
                print("display progress bar")
                progressBar.visibility = View.VISIBLE
            }

            .observeOn(backThread) // set thread for operation 2 and 3
            .map {
                /* operation 2 */
                print("calculating")
                Thread.sleep(5000)
                it.length
            }

            .doOnNext {
                /* operation 3 */
                print("finished calculating")
            }

            .observeOn(mainThread) // set thread for operation 4
            .doOnNext {
                /* operation 4 */
                print("hide progress bar and display result")
                progressBar.visibility = View.GONE
                resultTextView.text = "There're $it characters!"
            }

            .subscribe()

在上面的示例中,/* operation 1 */mainThread中运行,因为我们在其上方的行中使用observeOn(mainThread)对其进行了设置;然后我们通过再次调用backThread切换到observeOn,因此/* operation 2 */将在那里运行。因为我们在链接/* operation 3 */之前没有更改它,所以它也将在后线程中运行,就像/* operation 2 */一样;最后,我们再次调用observeOn(mainThread),以确保/* operation 4 */从主线程更新UI。

subscribeOn

因此,我们了解到observeOn为后续回调设置了线程。我们还缺少什么?好吧,Observable本身及其方法,例如just()create()subscribe()等,也是需要执行的代码。这就是对象沿流传递的方式。我们使用subscribeOn为与Observable本身相关的代码设置线程。

如果我们删除所有回调(由前面讨论过的observeOn控制),则将剩下“骨架代码”,默认情况下,它将在编写该代码的任何线程上运行(可能是主线程) :

    Observable.just("user1032613")
            .observeOn(mainThread)
            .doOnNext {
            }
            .observeOn(backThread)
            .map {
            }
            .doOnNext {
            }
            .observeOn(mainThread)
            .doOnNext {
            }
            .subscribe()

如果我们对在主线程上运行的空框架代码不满意,可以使用subscribeOn进行更改。例如,第一行Observable.just("user1032613")可能不像从我的用户名创建流那样简单-可能是来自Internet的字符串,或者您正在使用doOnSubscribe进行其他一些密集操作。在这种情况下,您可以调用subscribeOn(backThread)将一些代码放入另一个线程。

放置subscribeOn的位置

在编写此答案时,存在一些误解,如“仅调用一次”,“位置无关紧要”和“如果多次调用,则只有一次才重要”。经过大量的研究和实验,发现subscribeOn可以多次被有用地调用。

由于Observable使用Builder Pattern(“链接方法一个又一个”的花哨名称),因此subscribeOn的应用顺序相反。因此,此方法将线程设置为上面的代码,与observeOn完全相反。

我们可以使用doOnSubscribe方法进行实验。此方法在订阅事件上触发,并且在subscribeOn设置的线程上运行:

    Observable.just("user1032613")
            .doOnSubscribe {
                print("#3 running on main thread")
            }
            .subscribeOn(mainThread) // set thread for #3 and just()
            .doOnNext {
            }
            .map {
            }
            .doOnSubscribe {
                print("#2 running on back thread")
            }
            .doOnNext {
            }
            .subscribeOn(backThread) // set thread for #2 above
            .doOnNext {
            }
            .doOnSubscribe {
                print("#1 running on default thread")
            }
            .subscribe()

如果您从上至下阅读上述示例 ,则遵循逻辑可能会更容易,就像Builder Pattern执行代码的方式一样。

在此示例中,第一行Observable.just("user1032613")print("#3")在同一线程中运行,因为它们之间没有更多的subscribeOn。对于只关心just()create()内部代码的人来说,这会产生“只有第一个调用才重要”的错觉。这个quickly falls apart once you start doing more


脚注:

为简洁起见,示例中的线程和print()函数的定义如下:

val mainThread = AndroidSchedulers.mainThread()
val backThread = Schedulers.computation()
private fun print(msg: String) = Log.i("", "${Thread.currentThread().name}: $msg")

答案 3 :(得分:1)

这个答案并不新鲜,我只想澄清一点。

  1. 我们假设我们有两个线程。

     val pool1 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 1") }
     val pool2 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 2") }
    

  1. 如答案所述,observeOn将设置Downstream,而subscribeOn将设置Upstream。但是,如果两个都被使用怎么办?为了进行检查,我逐行添加了日志。

    Observable.just("what if use both")
     .doOnSubscribe { Log.d("Thread", "both, doOnSubscribe A " + Thread.currentThread().name) }
     .doOnNext { Log.d("Thread", "both, doOnNext A " + Thread.currentThread().name) }
     .map {
         Log.d("Thread", "both, map A " + Thread.currentThread().name)
         it + " A"
     }
    
     // observeOn
     .observeOn(Schedulers.from(pool1)) 
    
     .doOnSubscribe { Log.d("Thread", "both, doOnSubscribe B " + Thread.currentThread().name) }
     .doOnNext { Log.d("Thread", "both, doOnNext B " + Thread.currentThread().name) }
     .map {
         Log.d("Thread", "both, map B " + Thread.currentThread().name)
         it + " B"
     }
    
     // subscribeOn
     .subscribeOn(Schedulers.from(pool2)) 
    
     .doOnSubscribe { Log.d("Thread", "both, doOnSubscribe C " + Thread.currentThread().name) }
     .doOnNext { Log.d("Thread", "both, doOnNext C " + Thread.currentThread().name) }
     .map {
        Log.d("Thread", "both, map C " + Thread.currentThread().name)
        it + " C"
      }
    
     // observeOn main
     .observeOn(AndroidSchedulers.mainThread())
     .doOnNext { Log.d("Thread", "main " + Thread.currentThread().name) }
     .subscribe(
         { result -> Log.d("Thread", "main subscribe " + Thread.currentThread().name)}
         , { error -> {} }
         )
    

结果是:

both, doOnSubscribe C main
both, doOnSubscribe A Thread 2
both, doOnSubscribe B Thread 2

both, doOnNext A Thread 2
both, map A Thread 2

both, doOnNext B Thread 1
both, map B Thread 1

both, doOnNext C Thread 1
both, map C Thread 1

main main
main subscribe main
result: what if use both A B C

如您所见,doOnSubscribe从下到上首先调用。这意味着subscribe的优先级高于其他运算符,因此处理第一个代码的第一个线程是线程2

然后逐行调用其他运算符。 observeOn之后,线程已更改为Thread 1。然后,在调用subscribe之前,再次调用observeOn,以将线程更改为主线程。 (不必关心AndroidSchedulers,它只是一种调度程序)

TL; DR;

  • 第一个路径subscribeOn从下到上称为第一个。
  • 第二条路径observeOn从上到下与其他代码一起被调用。
  • RxJava2和RxJava3上的行为相同

答案 4 :(得分:0)

如果有人发现难以理解rx java描述(例如我),这就是纯java解释:

subscribeOn()

Observable.just("something")
  .subscribeOn(Schedulers.newThread())
  .subscribe(...);

等同于:

Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();

因为Observablesubscribe()上发出值,并且这里subscribe()在单独的线程中,所以这些值也与subscribe()在同一线程中发出。这就是为什么它在“上游”(影响先前操作的线程)和“下游”的原因。

observeOn()

Observable.just("something")
  .observeOn(Schedulers.newThread())
  .subscribe(...);

等同于:

Observable observable = Observable.just("something")
  .subscribe(it -> new Thread(() -> ...).start());

这里Observable在主线程中发出值,而在单独的线程中仅执行listener方法。

答案 5 :(得分:0)

当你订阅一个 observable 时,一个流开始向上到达链的顶部,然后再次返回。订阅部分与上行链接相关,观察部分与下行链接相关。

一旦到达链的顶端,订阅阶段就基本完成了。事件开始发出,映射、过滤器等的下行链被调用。

SubscribeOn 会影响其展示位置上方的 订阅 调用,例如 doOnSubscribe。

ObserveOn 影响其位置下方的 观察 调用,例如 doOnNext、map、flatmap 等。

两者都会改变用于继续向上或向下流动的线程。

import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;

import java.util.concurrent.CountDownLatch;

public class SubscribeVsObserveOn {

    public static void main(String[] args) throws InterruptedException {

        System.out.println("Ordinal 0: " + Thread.currentThread().getName());

        final CountDownLatch latch = new CountDownLatch(1);

        Observable
            .just("a regular string.")
            .doOnSubscribe(disposable ->
                System.out.println("Ordinal 2: " + Thread.currentThread().getName()))
            .subscribeOn(Schedulers.newThread())
            .observeOn(Schedulers.newThread())
            .doOnNext(s ->
                System.out.println("Ordinal 3: " + Thread.currentThread().getName()))
            .map(s -> s)
            .doOnSubscribe(disposable ->
                System.out.println("Ordinal 1: " + Thread.currentThread().getName()))
            .subscribeOn(Schedulers.newThread())
            .observeOn(Schedulers.newThread())
            .doOnNext(s ->
                System.out.println("Ordinal 4: " + Thread.currentThread().getName()))
            .map(s -> s)
            .subscribe(s -> latch.countDown());

        latch.await();
    }
}

输出如下:

Ordinal 0: main
Ordinal 1: RxNewThreadScheduler-1
Ordinal 2: RxNewThreadScheduler-2
Ordinal 3: RxNewThreadScheduler-3
Ordinal 4: RxNewThreadScheduler-4