- 原文出自《RxJava Essentials》
- 原文做者 : Ivan Morgillo
- 譯文出自 : 開發技術前線 www.devtf.cn
- 轉載聲明: 本譯文已受權開發者頭條享有獨家轉載權,未經容許,不得轉載!
- 譯者 : yuxingxin
- 項目地址 : RxJava-Essentials-CN
在上一章中,咱們探索了RxJava通用過濾方法。咱們學習瞭如何使用filter()
方法過濾咱們不須要的值,如何使用take()
獲得發射元素的子集,如何使用distinct()
函數來去除重複的。咱們學習瞭如何使用timeout()
,sample()
,以及debounce()
來利用時間。git
這一章中,咱們將學習如何變換可觀測序列來建立一個更好知足咱們需求的序列。github
RxJava提供了幾個mapping函數:map()
,flatMap()
,concatMap()
,flatMapIterable()
以及switchMap()
.全部這些函數都做用於一個可觀測序列,而後變換它發射的值,最後用一種新的形式返回它們。讓咱們用「真實世界」合適的例子一個個的學習下。緩存
RxJava的map
函數接收一個指定的Func
對象而後將它應用到每個由Observable發射的值上。下圖展現瞭如何將一個乘法函數應用到每一個發出的值上以此建立一個新的Observable來發射轉換的數據。app
考慮咱們已安裝的應用列表。咱們怎麼纔可以顯示一樣的列表,可是全部的名字都是小寫。eclipse
咱們的loadList()
函數能夠改爲這樣:async
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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 Observable<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
對象並提供一個名字小寫的新版本給觀察者。ide
在複雜的場景中,咱們有一個這樣的Observable:它發射一個數據序列,這些數據自己也能夠發射Observable。RxJava的flatMap()
函數提供一種鋪平序列的方式,而後合併這些Observables發射的數據,最後將合併後的結果做爲最終的Observable。函數
當咱們在處理可能有大量的Observables時,重要是記住任何一個Observables發生錯誤的狀況,flatMap()
函數將會觸發它本身的onError()
函數並放棄整個鏈。學習
重要的一點是關於合併部分:它容許交叉。正如上圖所示,這意味着flatMap()
函數在最後的Observable中不可以保證源Observables確切的發射順序。url
RxJava的concatMap()
函數解決了flatMap()
的交叉問題,提供了一種可以把發射的值連續在一塊兒的鋪平函數,而不是合併它們,以下圖所示:
做爲*map家族的一員,flatMapInterable()
和flatMap()
很像。僅有的本質不一樣是它將源數據兩兩結成對,而後生成Iterable而不是原始數據和生成的Observables。
以下圖所示,switchMap()
和flatMap()
很像,除了一點:當原始Observable發射一個新的數據(Observable)時,它將取消訂閱並中止監視以前那個數據的Observable產生的Observable,並開始監視當前這一個。
RxJava的scan()
函數能夠看作是一個累加器函數。scan()
函數對原始Observable發射的每一項數據都應用一個函數,它將函數的結果填充回可觀測序列,等待和下一次發射的數據一塊兒使用。
做爲一個通用的例子,給出一個累加器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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);
}
});
|
咱們獲得的結果是:
1
2
3
4
5
6
7
|
RXJAVA: item is: 1
RXJAVA: item is: 3
RXJAVA: item is: 6
RXJAVA: item is: 10
RXJAVA: item is: 15
RXJAVA: Sequence completed.
|
咱們也能夠建立一個新版本的loadList()
函數用來比較每一個安裝應用的名字從而建立一個名字長度遞增的列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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 Observable<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)
,就像下圖中的例子這樣:
拿第一個例子開始,咱們安裝的應用程序列表按照字母表的順序排序。然而,若是如今咱們想按照最近更新日期來排序咱們的App時該怎麼辦?RxJava提供了一個有用的函數從列表中按照指定的規則:groupBy()
來分組元素。下圖中的例子展現了groupBy()
如何將發射的值根據他們的形狀來進行分組。
這個函數將源Observable變換成一個發射Observables的新的Observable。它們中的每個新的Observable都發射一組指定的數據。
爲了建立一個分組了的已安裝應用列表,咱們在loadList()
函數中引入了一個新的元素:
1
2
3
4
5
6
7
8
9
|
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將全部的聯繫起來,像一般同樣而後訂閱它:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Observable.concat(groupedItems)
.subscribe(new Observable<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()
函數完成了,結果是:
RxJava中的buffer()
函數將源Observable變換一個新的Observable,這個新的Observable每次發射一組列表值而不是一個一個發射。
上圖中展現了buffer()
如何將count
做爲一個參數來指定有多少數據項被包在發射的列表中。實際上,buffer()
函數有幾種變體。其中有一個時容許你指定一個skip
值:此後每當收到skip項數據,用count項數據就填充緩存。以下圖所示:
buffer()
帶一個timespan
的參數,會建立一個每隔timespan時間段就會發射一個列表的Observable。
RxJava的window()
函數和buffer()
很像,可是它發射的時Observable而不是列表。下圖展現了window()
如何緩存3個數據項並把它們做爲一個新的Observable發射出去。
這些Observables中的每個都發射原始Observable數據的一個子集,數量由count
指定,最後發射一個onCompleted()
結束。正如buffer()
同樣,window()
也有一個skip
變體,以下圖所示:
RxJava的cast()
函數是本章中最後一個操做符。它是map()
操做符的特殊版本。它將源Observable中的每一項數據都轉換爲新的類型,把它變成了不一樣的Class
。
這一章中,咱們學習了RxJava時如何控制和轉換可觀測序列。用咱們如今所學的知識,咱們能夠建立、過濾、轉換咱們所想要的任何種類的可觀測序列。
下一章,咱們將學習如何組合Observable,合併它們,鏈接它們,再或者打包它們。