RxJava(三)轉換操作符
- RxJava(一)概述與基本使用
- RxJava(二)創建操作符
- RxJava(三)轉換操作符
上一篇博客已經講解了RxJava的API的基本使用,這裏我們來學習下RxJava的另一個特性——對數據流的轉換操作符。
RxJava中提供的操作符可以分爲轉換類操作符,過濾類操作符以及組合類操作符,接下來我將依次對這三種操作符的使用進行講解。
一、轉換類操作符
轉換類操作符顧名思義,就是將上游(被觀察者)發送的數據流進行轉換成另外一種形式(觀察者希望接收到的形式),從而使下游(觀察者)能夠正確的接受並作出響應。
這裏介紹map
,cast
,flatMap
,concatMap
,flatMapIterable
,switchMap
,scan
,buffer
,window
,groupBy
進行講解
map
map
的方法聲明爲:Observable<R> map(Func1<? super T, ? extends R> func)
,其中Func1<T, R>
接口裏面只包含一個R call(T t)
方法,從參數和返回值就可以看出來這是一個將T類型轉換成一個R類型的方法。FuncX
類的接口表示有X+1個泛型,前X個是call
方法的參數,最後一個是call
方法的返回值。
所以map
的作用就是將接收類型爲T的Observable
轉換爲接收類型爲R的Observable
RxJava官方給出的Map原理圖:
Map的使用也很簡單,比如說現在有一個Student列表,上游發出的數據流是Student格式,但是下游希望接收到的格式是Student對象的成績(Integer格式),代碼如下:
Observable.from(studentList)
.map(new Func1<Student, Integer>() {
@Override
public Integer call(Student student) {
return student.getGrade();
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
System.out.println("成績爲:" + integer);
}
});
上述代碼可以理解爲from
傳入數據流後,經過map
函數,轉換成Integer
,再到達subscriber
,調用事件回調函數。
cast
cast
運算符作用和map
類似,但是它只能將父類對象轉換爲子類對象,是map
的特殊版本。
方法聲明:Observable<R> cast(final Class<R> klass)
,將源事件類型轉換成klass
的數據類型,注意只能將父類對象轉爲子類對象
Person
是Student
的父類:
List<Person> personList = new ArrayList<>(studentList);
Observable.from(personList)
.cast(Student.class)
.subscribe(student -> System.out.println(student.getId()));
cast
執行轉化必須是可以轉的,如果該父類對象無法轉爲子類對象,會拋出ClassCastException
異常
原理圖如下:
flatMap
現在比如給出一個年級的所有班級的列表ArrayList<IClass>
,每個IClass
對象都包含內部學生的列表ArrayList<Student>
,我要輸出每個班人的成績。我當然可以這樣做:
Observable.from(iClassList)
.subscribe(new Action1<IClass>() {
@Override
public void call(IClass iClass) {
for (Student student : iClass.getStudentList()) {
System.out.println(student.getGrade());
}
}
});
那麼如果我不想在事件回調的時候去遍歷,我就想讓事件回調時接受到的就是每個Student,而不是一個班級IClass,該如何做呢?
這個時候就需要用到flatMap
這個操作符了,先看看具體的實現:
Observable.from(iClassList)
.flatMap(new Func1<IClass, Observable<Student>>() {
@Override
public Observable<Student> call(IClass iClass) {
return Observable.from(iClass.getStudentList());
}
})
.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
System.out.println(student.getGrade());
}
});
flatMap
的參數形式爲Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func)
,傳入一個T類型,返回一個接收事件爲R類型的Observable
,在結合上面的例子,就很好理解flatMap
的作用了。
上述代碼調用流程:
flatMap
依次將傳入的每一個iClass
對象轉換爲一個Observable<Student>
對象,在這個Observable
中傳入iClass.getStudentList()
事件列表- 最後將所有
Observable<Student>
對象中的事件都彙集到一個Observable
之中,這個Obsevable
就是flatMap
的返回值,由這個Observable
將所有的事件發給Subscriber
,Subscriber
再對每個事件依次調用事件回到函數。
flatMap
的缺點在於它並不一定會按照原始Observable
的順序發送事件,可能會有事件的交叉發送的情況。
原理圖如下:
concatMap
上面也講到了flatMap
的缺點,那麼如何解決交叉的問題呢?RxJava給我們提供了concatMap
這個方法,它用來將最初發送的事件按照原來的順序連續發送出去。使用方法跟flatMap
一樣。
Observable<R> concatMap(Func1<? super T, ? extends Observable<? extends R>> func)
原理圖如下:
flatMapIterable
flatMapIterable
跟flatMap
相差無幾,區別在於flatMapIterable
轉換後的是一個Iterable
對象,最後將所有的Iterable
都傳入一個Observable
中,由這個Observable
來將事件發送出去。
原理圖如下:
示例代碼如下:
Observable.from(iClassList)
.flatMapIterable(new Func1<IClass, Iterable<Student>>() {
@Override
public Iterable<Student> call(IClass iClass) {
return iClass.getStudentList();
}
})
.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
System.out.println(student.getGrade());
}
});
switchMap
switchMap
的作用也和flatMap
相似,但是它在連續發送事件的時候如果前一個事件未訂閱完成,後一個事件就已經到達,此時會將前一個事件直接取消,可多用於網絡請求之中。
Observable.just("first", "second", "third", "fourth", "fifth")
.switchMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
return Observable.just(s).subscribeOn(Schedulers.newThread());
}
})
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("success");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
System.out.println(s);
}
});
上述代碼每次產生事件都是新開一個線程,這樣有可能前面的事件在訂閱未完成時,後面的事件已經到來,這樣就會取消前一個事件,這種情況只有在併發的情況下才會發生,同一個線程事件先後發送執行是不會出現這種情況的。至於最後出現的結果,由於線程的調用取決於CPU的調度,所以會出現不同的情況,詳情可以參看RxJava的源碼分析。
原理圖如下:
scan
scan
就是將前一個事件產生的結果發送到下一個事件轉換時的第一個參數使用。
Observable<T> scan(Func2<T, T, T> accumulator)
Observable<R> scan(R initialValue, Func2<R, ? super T, R> accumulator)
Func2
接口的三個泛型,前兩個是內部call
方法的調用參數,第三個是call
的返回值
先說第一個重載,第一個T是前一個事件經過Func2.call
方法後的返回值,第二個T是當前事件,第三個T是調用call
方法後的返回值,作爲下一個事件的Func2
的第一個參數,至於第一個事件就是默認無初始計算結果了。第二個重載就是給第一個事件設置一個初始的計算結果。
代碼如下:
Observable.just(1, 2, 3, 4, 5)
.scan(new Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer sum, Integer item) {
return sum + item;
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}
@Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});
結果輸出爲:
Next: 1
Next: 3
Next: 6
Next: 10
Next: 15
Sequence complete.
原理圖:
buffer
buffer
就是將原有的事件轉換成一個List列表,由一個新的Observable
一次性發送出去,方法聲明如下:
Observable<List<T>> buffer(int count)
Observable<List<T>> buffer(int count, int skip)
第一種重載內部就是調用了buffer(count, count)
,所以就直接分析第二個重載。count
和skip
的含義爲:把每skip
個事件中的前count
個事件轉換成一個List列表,剩餘的丟棄,再由新的Observable將這個列表發送出去。比如現在有一個StudentList,id分別是從U1001到U1008,總共八個數據,如果調用如下代碼:
Observable.from(studentList)
.buffer(3, 4)
.subscribe(studentList1 -> {
for (Student student : studentList1) {
System.out.println(student.getId());
}
});
結果爲:
U1001
U1002
U1003
U1005
U1006
U1007
可見U1004和U1008被丟棄了,如果調用buffer(4,5)
,結果如下:
U1001
U1002
U1003
U1004
U1006
U1007
U1008
U1005是第一組5箇中的第5個被丟棄了,U1006-U1008是第二組5箇中的前三個,由於第4個不存在,所以發送第三個後就結束髮送了。
原理圖如下:
window
window
與buffer
的作用非常相似,區別在於buffer
發送出去的是一個列表List,而window
發送出去的事件是一個Observable
對象,方法聲明如下:
Observable<Observable<T>> window(int count)
Observable<Observable<T>> window(int count, int skip)
所以在window
的subscribe
中得到的是一個Observable
對象,需要再次調用subscribe
方法,去獲取其中的數據,代碼如下:
Observable.from(studentList)
.window(3, 5)
.subscribe(observable -> observable.subscribe(student -> System.out
.println(student.getId())));
以上是lambda表達式的寫法,不清楚的可以去學習一下Lambda表達式。
原理圖如下:
groupBy
groupBy
的常用調用方式如下:
Observable<GroupedObservable<K, T>> groupBy(final Func1<? super T, ? extends K> keySelector)
groupBy
通過keySelector
將發送的事件分爲不同的組,並將每一組以Observable<GroupObserverable>
的形式發送出去。keySelector
第一個參數爲call
方法傳入的事件類型,第二個參數是返回的key
比如發出的事件源爲學生列表,但是我想將其分爲及格的和不及格的分別處理,代碼如下
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable.from(studentList)
.groupBy(new Func1<Student, Integer>() {
@Override
public Integer call(Student student) {
return student.getGrade() >= 60 ? 1 : 0;
}
})
.subscribe(new Action1<GroupedObservable<Integer, Student>>() {
@Override
public void call(GroupedObservable<Integer, Student> integerStudentGroupedObservable) {
integerStudentGroupedObservable.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
if (integerStudentGroupedObservable.getKey() == 0) {
System.out.println("不及格的ID爲" + student.getId());
} else {
System.out.println("及格的ID爲" + student.getId());
}
}
});
}
});
運行代碼結果爲:
不及格的ID爲U1111
及格的ID爲U1112
不及格的ID爲U1113
及格的ID爲U1114
不及格的ID爲U1115
及格的ID爲U1116
不及格的ID爲U1117
及格的ID爲U1118
及格的ID爲U1119
原理圖如下: