我只是在学习Rx-java和Rxandroid2,我只是觉得在SubscribeOn和ObserveOn之间的主要区别是什么。
答案 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.just
,Observable.from
或Observable.range
注意:所有这些方法都接受值,因此不要使用阻塞方法来创建这些值,因为subscribeOn不会影响它。
如果要使用阻止功能,请使用
<强> Observable.defer(() -> Obervable.just(blockingMenthod())));
强>
重要事实:
subscribeOn不适用于Subjects
多个
subscribeOn
:
如果流中有多个subscribeOn
个实例,则只有第一个具有实际效果。
订阅&amp;
subscribeOn
人们认为subscribeOn
与Observable.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
设置线程用于回调“进一步向下(在其下方)”,例如doOnNext
或map
中的代码块。 / li>
subscribeOn
设置线程用于初始化“上游(在其上方)”,例如doOnSubscribe
,Observable.just
或Observable.create
。 / li>
让我们以一个示例来遍历此主题:我们想找到字符串“ user1032613”的长度。对于计算机而言,这不是一件容易的事,因此很自然地,我们会在后台线程中执行大量计算,以避免冻结应用程序。
我们可以根据需要多次调用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。
因此,我们了解到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)
这个答案并不新鲜,我只想澄清一点。
我们假设我们有两个线程。
val pool1 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 1") }
val pool2 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 2") }
如答案所述,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
从上到下与其他代码一起被调用。答案 4 :(得分:0)
如果有人发现难以理解rx java描述(例如我),这就是纯java解释:
Observable.just("something")
.subscribeOn(Schedulers.newThread())
.subscribe(...);
等同于:
Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();
因为Observable
在subscribe()
上发出值,并且这里subscribe()
在单独的线程中,所以这些值也与subscribe()
在同一线程中发出。这就是为什么它在“上游”(影响先前操作的线程)和“下游”的原因。
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