Rx第五章
轉(zhuǎn)換Observables
在上一章中挥下,我們探索了RxJava通用過濾方法趴泌。我們學(xué)習(xí)了如何使用filter()
方法過濾我們不需要的值堡牡,如何使用take()
得到發(fā)射元素的子集午绳,如何使用distinct()
函數(shù)來去除重復(fù)的筒溃。我們學(xué)習(xí)了如何借助timeout()
马篮,sample()
,以及debounce()
來利用時(shí)間怜奖。
這一章中浑测,我們將通過學(xué)習(xí)如何變換可觀測(cè)序列來創(chuàng)建一個(gè)能夠更好的滿足我們需求的序列。
*map家族
RxJava提供了幾個(gè)mapping函數(shù):map()
,flatMap()
,concatMap()
,flatMapIterable()
以及switchMap()
.所有這些函數(shù)都作用于一個(gè)可觀測(cè)序列歪玲,然后變換它發(fā)射的值迁央,最后用一種新的形式返回它們。讓我們用合適的“真實(shí)世界”的例子一個(gè)個(gè)的學(xué)習(xí)下滥崩。
Map
RxJava的map
函數(shù)接收一個(gè)指定的Func
對(duì)象然后將它應(yīng)用到每一個(gè)由Observable發(fā)射的值上岖圈。下圖展示了如何將一個(gè)乘法函數(shù)應(yīng)用到每個(gè)發(fā)出的值上以此創(chuàng)建一個(gè)新的Observable來發(fā)射轉(zhuǎn)換的數(shù)據(jù)。
考慮下我們已安裝的應(yīng)用列表钙皮。我們?cè)趺床拍軌蝻@示同樣的列表蜂科,但是又要所有的名字都小寫呢?
我們的loadList()
函數(shù)可以改成這樣:
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);
}
});
}
正如你看到的短条,像往常一樣創(chuàng)建我們發(fā)射的Observable之后导匣,我們追加一個(gè)map
調(diào)用,我們創(chuàng)建一個(gè)簡(jiǎn)單的函數(shù)來更新AppInfo
對(duì)象并提供一個(gè)名字小寫的新版本給觀察者慌烧。
FlatMap
在復(fù)雜的場(chǎng)景中逐抑,我們有一個(gè)這樣的Observable:它發(fā)射一個(gè)數(shù)據(jù)序列,這些數(shù)據(jù)本身也可以發(fā)射Observable屹蚊。RxJava的flatMap()
函數(shù)提供一種鋪平序列的方式厕氨,然后合并這些Observables發(fā)射的數(shù)據(jù),最后將合并后的結(jié)果作為最終的Observable汹粤。
當(dāng)我們?cè)谔幚砜赡苡写罅康腛bservables時(shí)命斧,重要是記住任何一個(gè)Observables發(fā)生錯(cuò)誤的情況,flatMap()
將會(huì)觸發(fā)它自己的onError()
函數(shù)并放棄整個(gè)鏈嘱兼。
重要的一點(diǎn)提示是關(guān)于合并部分:它允許交叉国葬。正如上圖所示,這意味著flatMap()
不能夠保證在最終生成的Observable中源Observables確切的發(fā)射順序芹壕。
ConcatMap
RxJava的concatMap()
函數(shù)解決了flatMap()
的交叉問題汇四,提供了一種能夠把發(fā)射的值連續(xù)在一起的鋪平函數(shù),而不是合并它們踢涌,如下圖所示:
FlatMapIterable
作為*map家族的一員通孽,flatMapInterable()
和flatMap()
很像。僅有的本質(zhì)不同是它將源數(shù)據(jù)兩兩結(jié)成對(duì)并生成Iterable睁壁,而不是原始數(shù)據(jù)項(xiàng)和生成的Observables背苦。
SwitchMap
如下圖所示互捌,switchMap()
和flatMap()
很像,除了一點(diǎn):每當(dāng)源Observable發(fā)射一個(gè)新的數(shù)據(jù)項(xiàng)(Observable)時(shí)行剂,它將取消訂閱并停止監(jiān)視之前那個(gè)數(shù)據(jù)項(xiàng)產(chǎn)生的Observable秕噪,并開始監(jiān)視當(dāng)前發(fā)射的這一個(gè)。
Scan
RxJava的scan()
函數(shù)可以看做是一個(gè)累積函數(shù)厚宰。scan()
函數(shù)對(duì)原始Observable發(fā)射的每一項(xiàng)數(shù)據(jù)都應(yīng)用一個(gè)函數(shù)腌巾,計(jì)算出函數(shù)的結(jié)果值,并將該值填充回可觀測(cè)序列固阁,等待和下一次發(fā)射的數(shù)據(jù)一起使用壤躲。
作為一個(gè)通用的例子,給出一個(gè)累加器:
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);
}
});
我們得到的結(jié)果是:
RXJAVA: item is: 1
RXJAVA: item is: 3
RXJAVA: item is: 6
RXJAVA: item is: 10
RXJAVA: item is: 15
RXJAVA: Sequence completed.
我們也可以創(chuàng)建一個(gè)新版本的loadList()
函數(shù)用來比較每個(gè)安裝應(yīng)用的名字從而創(chuàng)建一個(gè)名字長(zhǎng)度遞增的列表备燃。
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);
}
});
}
結(jié)果如下:
有一個(gè)scan()
函數(shù)的變體,它用初始值作為第一個(gè)發(fā)射的值凌唬,方法簽名看起來像:scan(R,Func2)
并齐,如下圖中的例子這樣:
GroupBy
拿第一個(gè)例子開始,我們安裝的應(yīng)用程序列表按照字母表的順序排序客税。然而况褪,如果現(xiàn)在我們想按照最近更新日期來排序我們的App時(shí)該怎么辦?RxJava提供了一個(gè)有用的函數(shù)從列表中按照指定的規(guī)則:groupBy()
來分組元素更耻。下圖中的例子展示了groupBy()
如何將發(fā)射的值根據(jù)他們的形狀來進(jìn)行分組测垛。
這個(gè)函數(shù)將源Observable變換成一個(gè)發(fā)射Observables的新的Observable。它們中的每一個(gè)新的Observable都發(fā)射一組指定的數(shù)據(jù)秧均。
為了創(chuàng)建一個(gè)分組了的已安裝應(yīng)用列表食侮,我們?cè)?code>loadList()函數(shù)中引入了一個(gè)新的元素:
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()));
}
});
現(xiàn)在我們創(chuàng)建了一個(gè)新的Observable,groupedItems
目胡,它將會(huì)發(fā)射一個(gè)帶有GroupedObservable
的序列锯七。GroupedObservable
是一個(gè)特殊的Observable,它源自一個(gè)分組的key誉己。在這個(gè)例子中眉尸,key就是String
,代表的意思是Month/Year
格式化的最近更新日期巨双。
這一點(diǎn)噪猾,我們已經(jīng)創(chuàng)建了幾個(gè)發(fā)射AppInfo
數(shù)據(jù)的Observable,用來填充我們的列表筑累。我們想保留字母排序和分組排序袱蜡。我們將創(chuàng)建一個(gè)新的Observable將所有的聯(lián)系起來,像往常一樣然后訂閱它:
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()
函數(shù)完成了疼阔,結(jié)果是:
Buffer
RxJava中的buffer()
函數(shù)將源Observable變換一個(gè)新的Observable戒劫,這個(gè)新的Observable每次發(fā)射一組列表值而不是一個(gè)一個(gè)發(fā)射半夷。
上圖中展示了buffer()
如何將count
作為一個(gè)參數(shù)來指定有多少數(shù)據(jù)項(xiàng)被包在發(fā)射的列表中。實(shí)際上迅细,buffer()
函數(shù)有幾種變體巫橄。其中有一個(gè)是允許你指定一個(gè)skip
值:此后每skip
項(xiàng)數(shù)據(jù),然后又用count項(xiàng)數(shù)據(jù)填充緩沖區(qū)茵典。如下圖所示:
buffer()
帶一個(gè)timespan
的參數(shù)湘换,會(huì)創(chuàng)建一個(gè)每隔timespan時(shí)間段就會(huì)發(fā)射一個(gè)列表的Observable。
Window
RxJava的window()
函數(shù)和buffer()
很像统阿,但是它發(fā)射的是Observable而不是列表彩倚。下圖展示了window()
如何緩存3個(gè)數(shù)據(jù)項(xiàng)并把它們作為一個(gè)新的Observable發(fā)射出去。
這些Observables中的每一個(gè)都發(fā)射原始Observable數(shù)據(jù)的一個(gè)子集扶平,數(shù)量由count
指定,最后發(fā)射一個(gè)onCompleted()
結(jié)束帆离。正如buffer()
一樣,window()
也有一個(gè)skip
變體,如下圖所示:
Cast
RxJava的cast()
函數(shù)是本章中最后一個(gè)操作符。它是map()
操作符的特殊版本结澄。它將源Observable中的每一項(xiàng)數(shù)據(jù)都轉(zhuǎn)換為新的類型哥谷,把它變成了不同的Class
。
總結(jié)
這一章中麻献,我們學(xué)習(xí)了RxJava時(shí)如何控制和轉(zhuǎn)換可觀測(cè)序列们妥。用我們現(xiàn)在所學(xué)的知識(shí),我們可以創(chuàng)建勉吻、過濾监婶、轉(zhuǎn)換我們所想要的任何種類的可觀測(cè)序列。
下一章齿桃,我們將學(xué)習(xí)如何組合Observable惑惶,合并它們,連接它們源譬,再或者打包它們集惋。