我學(xué) rxjava 2(4)- subscribeOn/observeOn 切換的是誰的線程

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 可以多次切換他之后的操作符的線程

參考資料:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容