rxjava 的東西是很多的,難免有理解錯誤的地方,這兩天面試碰到有人問 subscribeOn/observeOn 線程切換的問題,我回答完,面試官明顯不滿意,回來找了找資料,還真是自己理解錯了,有必要專門寫一篇文章出來。
例子1 : subscribeOn/observeOn 最簡單使用
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d("AA", "數(shù)據(jù)源" + Thread.currentThread().getName());
emitter.onNext("");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d("AA", "監(jiān)聽者" + Thread.currentThread().getName());
}
});
Snip20180421_12.png
按照之前的理解:
- subscribeOn 是決定 observable 中產(chǎn)生數(shù)據(jù)的方法執(zhí)行在哪個(gè)線程
- observeOn 是決定 observer 消費(fèi)數(shù)據(jù)的方法執(zhí)行在哪個(gè)線程
我們看這個(gè)最簡單的例子,的確是這樣,那么更復(fù)雜的情況呢。
例子2:subscribeOn/observeOn 連著重復(fù)寫,哪個(gè)為準(zhǔn)
還是以上面那個(gè)最簡單的例子來
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d("AA", "數(shù)據(jù)源" + Thread.currentThread().getName());
emitter.onNext("");
}
})
.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d("AA", "監(jiān)聽者" + Thread.currentThread().getName());
}
});
Snip20180421_13.png
從結(jié)果來看:
- 多個(gè) subscribeOn 連著寫,以第一個(gè)為準(zhǔn)
- 多個(gè) observeOn 連著寫,以最后一個(gè)為準(zhǔn)
例子3 :添加多個(gè)操作符呢
rxjava 中的操作符基本都會生成一個(gè)新的 observable 出來,上下游的關(guān)系就復(fù)雜了,情況會不會有變化呢,這個(gè)例子就復(fù)雜了
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d("AA", "數(shù)據(jù)源" + Thread.currentThread().getName());
emitter.onNext("");
}
})
.subscribeOn(Schedulers.computation())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第1次變化" + Thread.currentThread().getName());
return "";
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第2次變化" + Thread.currentThread().getName());
return "";
}
})
.observeOn(Schedulers.io())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第3次變化" + Thread.currentThread().getName());
return "";
}
})
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第4次變化" + Thread.currentThread().getName());
return "";
}
})
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d("AA", "監(jiān)聽者" + Thread.currentThread().getName());
}
});
Snip20180421_17.png
從結(jié)果看:
- subscribeOn 決定 Observable.create 的執(zhí)行線程,之后再寫 subscribeOn ,無論是挨著寫,還是隔著操作符寫都沒有意思
- subscribeOn 決定數(shù)據(jù)源的執(zhí)行線程后,也會當(dāng)前線程置為這個(gè)線程,若無其他設(shè)置,之后操作符的操作也是在當(dāng)前線程執(zhí)行,也就是 subscribeOn 指定的線程
- observeOn 不僅僅可以決定 .subscribe 執(zhí)行的線程,更能夠更改 observeOn 之后書寫的操作符的執(zhí)行線程,也就是可以切換當(dāng)前線程。
例子4:用 observeOn 給多個(gè)操作符切換線程
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d("AA", "數(shù)據(jù)源" + Thread.currentThread().getName());
emitter.onNext("");
}
})
.subscribeOn(Schedulers.computation())
.observeOn( Schedulers.io() )
.observeOn(AndroidSchedulers.mainThread())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第1次變化" + Thread.currentThread().getName());
return "";
}
})
.observeOn(Schedulers.io())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第2次變化" + Thread.currentThread().getName());
return "";
}
})
.observeOn(Schedulers.computation())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第3次變化" + Thread.currentThread().getName());
return "";
}
})
.observeOn(AndroidSchedulers.mainThread())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
Log.d("AA", "第4次變化" + Thread.currentThread().getName());
return "";
}
})
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d("AA", "監(jiān)聽者" + Thread.currentThread().getName());
}
});
Snip20180421_19.png
從結(jié)果看:
- observeOn 的的確確是可以 rxjava 所在線程
好了來說說原理
因?yàn)?observeOn() 指定的是 Subscriber 的線程,而這個(gè) Subscriber 并不是(嚴(yán)格說應(yīng)該為『不一定是』,但這里不妨理解為『不是』)subscribe() 參數(shù)中的 Subscriber ,而是 observeOn() 執(zhí)行時(shí)的當(dāng)前 Observable 所對應(yīng)的 Subscriber ,即它的直接下級 Subscriber 。換句話說,observeOn() 指定的是它之后的操作所在的線程。因此如果有多次切換線程的需求,只要在每個(gè)想要切換線程的位置調(diào)用一次 observeOn() 即可。 ——扔物線
摘自:擁抱RxJava(番外篇):關(guān)于RxJava的Tips & Tricks ,推薦大家去看看原文
我們翻翻源碼呢,看看能不能簡單的走一下邏輯
1.png
2.png
3.png
- 我們可以看到 map 是生成了一個(gè)新的 observable 出來,這個(gè) observable 還有我們的變化數(shù)據(jù)的接口類
- 在這個(gè) 新的 observable 里面,有上面的 observable 對象引用,然后給這個(gè)上面的 observable 對象注冊了一個(gè)新的觀察者進(jìn)來
- 這個(gè)新的觀察者即是一個(gè) observer,但同時(shí)還是一個(gè) observable ,這個(gè)新的觀察者在數(shù)據(jù)生成方法中接受上一級 observable 發(fā)送過來的數(shù)據(jù),然后根據(jù)我們傳入的數(shù)據(jù)變換接口對象計(jì)算出新的數(shù)據(jù),最后發(fā)送給消費(fèi)者或是下一級
不是很好理解,但是大概應(yīng)該是這個(gè)意思
換個(gè)更容易理解的描述:
- subscribeOn 決定的是上游線程,上游切換到哪個(gè)線程,下游要是不改的話,rxjava 就在這個(gè)線程一直跑
- observeOn 決定的是下游線程
- 整個(gè) rxjava 中嚴(yán)格說來真正的上游只有一個(gè),那就是產(chǎn)生數(shù)據(jù)的位置,比如 .just / ,create,其他任何變換和操作符,注冊都是下游。
- 所以 subscribeOn 只有第一次切換有效,作用范圍也是最小的,就是 .just / ,create
- 基本上操作符都會生成一個(gè)新的 observable 出來,和之前的 observable 關(guān)聯(lián)(其實(shí)也是注冊到之前的 observable)。所以在一個(gè)操作的范圍來看,前一個(gè) observable 發(fā)送數(shù)據(jù)給我,算是上游,我這個(gè)操作符消費(fèi)數(shù)據(jù),產(chǎn)生新的 observable ,算是下游
- 所以 observeOn 可以多次切換他之后的操作符的線程