給初學者的RxJava2.0教程(三)

Outlinejava

[TOC]api

前言

上一節講解了線程調度, 而且舉了兩個實際中的例子, 其中有一個登陸的例子, 不知你們有沒有想過這麼一個問題, 若是是一個新用戶, 必須先註冊, 等註冊成功以後再自動登陸該怎麼作呢. 網絡

很明顯, 這是一個嵌套的網絡請求, 首先須要去請求註冊, 待註冊成功回調了再去請求登陸的接口.app

咱們固然能夠想固然的寫成這樣:ide

private void login() {
        api.login(new LoginRequest())
                .subscribeOn(Schedulers.io())               //在IO線程進行網絡請求
                .observeOn(AndroidSchedulers.mainThread())  //回到主線程去處理請求結果
                .subscribe(new Consumer<LoginResponse>() {
                    @Override
                    public void accept(LoginResponse loginResponse) throws Exception {
                        Toast.makeText(MainActivity.this, "登陸成功", Toast.LENGTH_SHORT).show();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Toast.makeText(MainActivity.this, "登陸失敗", Toast.LENGTH_SHORT).show();
                    }
                });
    }

    private void register() {
        api.register(new RegisterRequest())
                .subscribeOn(Schedulers.io())               //在IO線程進行網絡請求
                .observeOn(AndroidSchedulers.mainThread())  //回到主線程去處理請求結果
                .subscribe(new Consumer<RegisterResponse>() {
                    @Override
                    public void accept(RegisterResponse registerResponse) throws Exception {
                        Toast.makeText(MainActivity.this, "註冊成功", Toast.LENGTH_SHORT).show();
                        login();   //註冊成功, 調用登陸的方法
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Toast.makeText(MainActivity.this, "註冊失敗", Toast.LENGTH_SHORT).show();
                    }
                });
    }複製代碼

(其實能寫成這樣的代碼的人也很不錯了, 至少沒寫到一塊兒...)函數

這樣的代碼可以工做, 但不夠優雅, 經過本節的學習, 可讓咱們用一種更優雅的方式來解決這個問題.學習

正題

先來看看最簡單的變換操做符map吧this

Map

map是RxJava中最簡單的一個變換操做符了, 它的做用就是對上游發送的每個事件應用一個函數, 使得每個事件都按照指定的函數去變化. 用事件圖表示以下:spa

map.png

圖中map中的函數做用是將圓形事件轉換爲矩形事件, 從而致使下游接收到的事件就變爲了矩形.用代碼來表示這個例子就是:線程

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
            }
        }).map(new Function<Integer, String>() {
            @Override
            public String apply(Integer integer) throws Exception {
                return "This is result " + integer;
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.d(TAG, s);
            }
        });複製代碼

在上游咱們發送的是數字類型, 而在下游咱們接收的是String類型, 中間起轉換做用的就是map操做符, 運行結果爲:

D/TAG: This is result 1 
 D/TAG: This is result 2 
 D/TAG: This is result 3複製代碼

經過Map, 能夠將上游發來的事件轉換爲任意的類型, 能夠是一個Object, 也能夠是一個集合, 如此強大的操做符你難道不想試試?

接下來咱們來看另一個廣爲人知的操做符flatMap.

FlatMap

flatMap是一個很是強大的操做符, 先用一個比較難懂的概念說明一下:

FlatMap將一個發送事件的上游Observable變換爲多個發送事件的Observables,而後將它們發射的事件合併後放進一個單獨的Observable裏.

這句話比較難以理解, 咱們先通俗易懂的圖片來詳細的講解一下, 首先先來看看總體的一個圖片:

flatmap.png

先看看上游, 上游發送了三個事件, 分別是1,2,3, 注意它們的顏色.

中間flatMap的做用是將圓形的事件轉換爲一個發送矩形事件和三角形事件的新的上游Observable.

仍是不能理解? 別急, 再來看看分解動做:

flatmap1.png

這樣就很好理解了吧 !!!

上游每發送一個事件, flatMap都將建立一個新的水管, 而後發送轉換以後的新的事件, 下游接收到的就是這些新的水管發送的數據. 這裏須要注意的是, flatMap並不保證事件的順序, 也就是圖中所看到的, 並非事件1就在事件2的前面. 若是須要保證順序則須要使用concatMap.

說了原理, 咱們仍是來看看實際中的代碼如何寫吧:

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
            }
        }).flatMap(new Function<Integer, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(Integer integer) throws Exception {
                final List<String> list = new ArrayList<>();
                for (int i = 0; i < 3; i++) {
                    list.add("I am value " + integer);
                }
                return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.d(TAG, s);
            }
        });複製代碼

如代碼所示, 咱們在flatMap中將上游發來的每一個事件轉換爲一個新的發送三個String事件的水管, 爲了看到flatMap結果是無序的,因此加了10毫秒的延時, 來看看運行結果吧:

D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 3
D/TAG: I am value 3
D/TAG: I am value 3
D/TAG: I am value 2
D/TAG: I am value 2
D/TAG: I am value 2複製代碼

結果也確實驗證了咱們以前所說.

這裏也簡單說一下concatMap吧, 它和flatMap的做用幾乎如出一轍, 只是它的結果是嚴格按照上游發送的順序來發送的, 來看個代碼吧:

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
            }
        }).concatMap(new Function<Integer, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(Integer integer) throws Exception {
                final List<String> list = new ArrayList<>();
                for (int i = 0; i < 3; i++) {
                    list.add("I am value " + integer);
                }
                return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.d(TAG, s);
            }
        });複製代碼

只是將以前的flatMap改成了concatMap, 其他原封不動, 運行結果以下:

D/TAG: I am value 1   
D/TAG: I am value 1   
D/TAG: I am value 1   
D/TAG: I am value 2   
D/TAG: I am value 2   
D/TAG: I am value 2   
D/TAG: I am value 3   
D/TAG: I am value 3   
D/TAG: I am value 3複製代碼

能夠看到, 結果仍然是有序的.

好了關於RxJava的操做符最基本的使用就講解到這裏了, RxJava中內置了許許多多的操做符, 這裏經過講解mapflatMap只是起到一個拋磚引玉的做用, 關於其餘的操做符只要你們按照本文的思路去理解, 再仔細閱讀文檔, 應該是沒有問題的了, 若是你們有須要也能夠將須要講解的操做符列舉出來, 我能夠根據你們的需求講解一下.

實踐

學習了FlatMap操做符, 咱們就能夠回答文章開頭提出的那個問題了.

如何優雅的解決嵌套請求, 只須要用flatMap轉換一下就好了.

先回顧一下上一節的請求接口:

public interface Api {
    @GET
    Observable<LoginResponse> login(@Body LoginRequest request);

    @GET
    Observable<RegisterResponse> register(@Body RegisterRequest request);
}複製代碼

能夠看到登陸和註冊返回的都是一個上游Observable, 而咱們的flatMap操做符的做用就是把一個Observable轉換爲另外一個Observable, 所以結果就很顯而易見了:

api.register(new RegisterRequest())            //發起註冊請求
                .subscribeOn(Schedulers.io())               //在IO線程進行網絡請求
                .observeOn(AndroidSchedulers.mainThread())  //回到主線程去處理請求註冊結果
                .doOnNext(new Consumer<RegisterResponse>() {
                    @Override
                    public void accept(RegisterResponse registerResponse) throws Exception {
                        //先根據註冊的響應結果去作一些操做
                    }
                })
                .observeOn(Schedulers.io())                 //回到IO線程去發起登陸請求
                .flatMap(new Function<RegisterResponse, ObservableSource<LoginResponse>>() {
                    @Override
                    public ObservableSource<LoginResponse> apply(RegisterResponse registerResponse) throws Exception {
                        return api.login(new LoginRequest());
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())  //回到主線程去處理請求登陸的結果
                .subscribe(new Consumer<LoginResponse>() {
                    @Override
                    public void accept(LoginResponse loginResponse) throws Exception {
                        Toast.makeText(MainActivity.this, "登陸成功", Toast.LENGTH_SHORT).show();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Toast.makeText(MainActivity.this, "登陸失敗", Toast.LENGTH_SHORT).show();
                    }
                });複製代碼

從這個例子也能夠看到咱們切換線程是多麼簡單.

好了本次的教程就到這裏了. 下一節咱們將會學到Flowable 以及理解Backpressure背壓的概念. 敬請期待.

相關文章
相關標籤/搜索