Rx第五章

Rx第五章

轉換Observables

在上一章中,我們探索了RxJava通用過濾方法。我們學習了如何使用filter()方法過濾我們不需要的值,如何使用take()得到發射元素的子集,如何使用distinct()函數來去除重復的。我們學習了如何借助timeout()sample(),以及debounce()來利用時間。

這一章中,我們將通過學習如何變換可觀測序列來創建一個能夠更好的滿足我們需求的序列。

*map家族

RxJava提供了幾個mapping函數:map(),flatMap(),concatMap(),flatMapIterable()以及switchMap().所有這些函數都作用于一個可觀測序列,然后變換它發射的值,最后用一種新的形式返回它們。讓我們用合適的“真實世界”的例子一個個的學習下。

Map

RxJava的map函數接收一個指定的Func對象然后將它應用到每一個由Observable發射的值上。下圖展示了如何將一個乘法函數應用到每個發出的值上以此創建一個新的Observable來發射轉換的數據。

考慮下我們已安裝的應用列表。我們怎么才能夠顯示同樣的列表,但是又要所有的名字都小寫呢?

我們的loadList()函數可以改成這樣:

private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
        .map(new Func1<AppInfo,AppInfo>(){
            @Override
            public Appinfo call(AppInfo appInfo){
                String currentName = appInfo.getName();
                String lowerCaseName = currentName.toLowerCase();
                appInfo.setName(lowerCaseName);
                return appInfo;
            }
        })
        .subscribe(new Observer<AppInfo>() {

            @Override
            public void onCompleted() {
                mSwipeRefreshLayout.setRefreshing(false);
            }

            @Override
            public void onError(Throwable e) {
                Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                mSwipeRefreshLayout.setRefreshing(false);
            }

            @Override
            public void onNext(AppInfo appInfo) {
                mAddedApps.add(appInfo); 
                mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
            }
        });
}

正如你看到的,像往常一樣創建我們發射的Observable之后,我們追加一個map調用,我們創建一個簡單的函數來更新AppInfo對象并提供一個名字小寫的新版本給觀察者。

FlatMap

在復雜的場景中,我們有一個這樣的Observable:它發射一個數據序列,這些數據本身也可以發射Observable。RxJava的flatMap()函數提供一種鋪平序列的方式,然后合并這些Observables發射的數據,最后將合并后的結果作為最終的Observable。

當我們在處理可能有大量的Observables時,重要是記住任何一個Observables發生錯誤的情況,flatMap()將會觸發它自己的onError()函數并放棄整個鏈。

重要的一點提示是關于合并部分:它允許交叉。正如上圖所示,這意味著flatMap()不能夠保證在最終生成的Observable中源Observables確切的發射順序。

ConcatMap

RxJava的concatMap()函數解決了flatMap()的交叉問題,提供了一種能夠把發射的值連續在一起的鋪平函數,而不是合并它們,如下圖所示:

FlatMapIterable

作為*map家族的一員,flatMapInterable()flatMap()很像。僅有的本質不同是它將源數據兩兩結成對并生成Iterable,而不是原始數據項和生成的Observables。

SwitchMap

如下圖所示,switchMap()flatMap()很像,除了一點:每當源Observable發射一個新的數據項(Observable)時,它將取消訂閱并停止監視之前那個數據項產生的Observable,并開始監視當前發射的這一個。

Scan

RxJava的scan()函數可以看做是一個累積函數。scan()函數對原始Observable發射的每一項數據都應用一個函數,計算出函數的結果值,并將該值填充回可觀測序列,等待和下一次發射的數據一起使用。

作為一個通用的例子,給出一個累加器:

Observable.just(1,2,3,4,5)
        .scan((sum,item) -> sum + item)
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.d("RXJAVA", "Sequence completed.");
            }

            @Override
            public void onError(Throwable e) {
                Log.e("RXJAVA", "Something went south!");
            }

            @Override
            public void onNext(Integer item) {
                Log.d("RXJAVA", "item is: " + item);
            }
        });

我們得到的結果是:

RXJAVA: item is: 1
RXJAVA: item is: 3
RXJAVA: item is: 6
RXJAVA: item is: 10
RXJAVA: item is: 15
RXJAVA: Sequence completed.

我們也可以創建一個新版本的loadList()函數用來比較每個安裝應用的名字從而創建一個名字長度遞增的列表。

private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
            .scan((appInfo,appInfo2) -> {
                if(appInfo.getName().length > appInfo2.getName().length()){
                    return appInfo;
                } else {
                    return appInfo2;
                }
            })
            .distinct()
            .subscribe(new Observer<AppInfo>() {

                @Override
                public void onCompleted() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                @Override
                public void onError(Throwable e) {
                    Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                @Override
                public void onNext(AppInfo appInfo) {
                    mAddedApps.add(appInfo); 
                    mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
                }
            });
}

結果如下:

有一個scan()函數的變體,它用初始值作為第一個發射的值,方法簽名看起來像:scan(R,Func2),如下圖中的例子這樣:

GroupBy

拿第一個例子開始,我們安裝的應用程序列表按照字母表的順序排序。然而,如果現在我們想按照最近更新日期來排序我們的App時該怎么辦?RxJava提供了一個有用的函數從列表中按照指定的規則:groupBy()來分組元素。下圖中的例子展示了groupBy()如何將發射的值根據他們的形狀來進行分組。

這個函數將源Observable變換成一個發射Observables的新的Observable。它們中的每一個新的Observable都發射一組指定的數據。

為了創建一個分組了的已安裝應用列表,我們在loadList()函數中引入了一個新的元素:

Observable<GroupedObservable<String,AppInfo>> groupedItems = Observable.from(apps)
    .groupBy(new Func1<AppInfo,String>(){
        @Override
        public String call(AppInfo appInfo){
            SimpleDateFormat formatter = new SimpleDateFormat("MM/yyyy");
            return formatter.format(new Date(appInfo.getLastUpdateTime()));
        }
    });

現在我們創建了一個新的Observable,groupedItems,它將會發射一個帶有GroupedObservable的序列。GroupedObservable是一個特殊的Observable,它源自一個分組的key。在這個例子中,key就是String,代表的意思是Month/Year格式化的最近更新日期。

這一點,我們已經創建了幾個發射AppInfo數據的Observable,用來填充我們的列表。我們想保留字母排序和分組排序。我們將創建一個新的Observable將所有的聯系起來,像往常一樣然后訂閱它:

Observable.concat(groupedItems)
    .subscribe(new Observer<AppInfo>() {

        @Override
        public void onCompleted() {
            mSwipeRefreshLayout.setRefreshing(false);
        }

        @Override
        public void onError(Throwable e) {
            Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
            mSwipeRefreshLayout.setRefreshing(false);
        }

        @Override
        public void onNext(AppInfo appInfo) {
            mAddedApps.add(appInfo); 
            mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
        }
    });

我們的loadList()函數完成了,結果是:

Buffer

RxJava中的buffer()函數將源Observable變換一個新的Observable,這個新的Observable每次發射一組列表值而不是一個一個發射。

上圖中展示了buffer()如何將count作為一個參數來指定有多少數據項被包在發射的列表中。實際上,buffer()函數有幾種變體。其中有一個是允許你指定一個skip值:此后每skip項數據,然后又用count項數據填充緩沖區。如下圖所示:

buffer()帶一個timespan的參數,會創建一個每隔timespan時間段就會發射一個列表的Observable。

Window

RxJava的window()函數和buffer()很像,但是它發射的是Observable而不是列表。下圖展示了window()如何緩存3個數據項并把它們作為一個新的Observable發射出去。

這些Observables中的每一個都發射原始Observable數據的一個子集,數量由count指定,最后發射一個onCompleted()結束。正如buffer()一樣,window()也有一個skip變體,如下圖所示:

Cast

RxJava的cast()函數是本章中最后一個操作符。它是map()操作符的特殊版本。它將源Observable中的每一項數據都轉換為新的類型,把它變成了不同的Class

總結

這一章中,我們學習了RxJava時如何控制和轉換可觀測序列。用我們現在所學的知識,我們可以創建、過濾、轉換我們所想要的任何種類的可觀測序列。

下一章,我們將學習如何組合Observable,合并它們,連接它們,再或者打包它們。

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

推薦閱讀更多精彩內容

  • 本篇文章介主要紹RxJava中操作符是以函數作為基本單位,與響應式編程作為結合使用的,對什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,890評論 0 10
  • 響應式編程簡介 響應式編程是一種基于異步數據流概念的編程模式。數據流就像一條河:它可以被觀測,被過濾,被操作,或者...
    說碼解字閱讀 3,104評論 0 5
  • 作者: maplejaw本篇只解析標準包中的操作符。對于擴展包,由于使用率較低,如有需求,請讀者自行查閱文檔。 創...
    maplejaw_閱讀 45,822評論 8 93
  • 創建操作 用于創建Observable的操作符Create通過調用觀察者的方法從頭創建一個ObservableEm...
    rkua閱讀 1,867評論 0 1
  • 1,text field(文本框)的onKeyUp事件,實現每輸入一個字,觸發一次事件 (輸入框用textFile...
    y煙雨任平生閱讀 298評論 0 0