RXJava 常用操作符整理(持續更新)
參考文章:
扔物線大哥神作(對於轉換的原理及RXJava基礎講的非常透徹):
http://gank.io/post/560e15be2dca930e00da1083
操作符系列好文(系統學習操作符的不二選擇):
http://blog.chinaunix.net/uid-20771867-id-5187376.html
RXJava最爽的莫過於鏈式程式設計,程式碼條理清晰,可以把各種回撥巢狀、時序混亂、型別轉換等的問題以一條鏈式呼叫統統搞定。而這麼神奇的功能就需要操作符來支援。
看一段簡單的程式碼:
Observable.just("張三","李四","王五").subscribe(new Action1<String>()
{
@Override public void call(String s)
{
Log.d("name",s);
}
});
就是輸出幾個簡單的字串,其中just就是一個操作符(同理create、from)也是。在我理解中,操作符就是在資料流中,對資料進行各種轉換、處理的一些封裝好的方法。
而我們常用的操作符,除了剛才說的create、from、just這些,最常用的就是用於資料轉換處理(也可以用於其他用途)的map和flatMap了。除此之外,還有可以幫我們解決蒐集多個API結果狀態的merge啊(吐血推薦)、防抖throttleFirst啊(再也不用不停地getCurrentTime了)等等。。。
Map
如果有一個模組,可以獲取到每個使用者的所有資訊,然後在UI上我需要繪製使用者的頭像,這樣需要在使用者資訊裡獲取到頭像的Url。這個使用者資訊包括了:
class UserInfoBean { public String avatar; public String name; public String phoneNum; }
那如果按照正常的辦法,我們會這樣去完成:
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
Observable.just(userInfo).subscribe(new Action1<UserInfoBean>()
{
@Override public void call(UserInfoBean bean)
{
Glide.with(mContext).load(bean.avatarUrl).into(ivAvatar);
}
});
講道理說subscriber中應該只響應結果,這裡的subsriber只期望獲取一個url用來展示頭像,而對其他的東西並不感興趣,那麼好吧,修改一下程式碼:
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
Observable.just(userInfo.avatarUrl).subscribe(new Action1<String>()
{
@Override public void call(String url)
{
Glide.with(mContext).load(url).into(ivAvatar);
}
});
在這裡,我們使observable中呼叫next的時候直接傳入一個頭像的Url,這樣不就好了嘛~ 可是,如果我要在其他地方需要獲取到使用者的名字怎麼辦?現在這個Observable中直接返回的是頭像Url啊 ,難道我要重寫一個嗎?
這時候就可以用到map了。
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
TextView tvName = (TextView)findViewById(R.id.tv_user_name);
//更新頭像
getUserInfo().map(new Func1<UserInfoBean,String>()
{
@Override public String call(UserInfoBean bean)
{
return bean.avatarUrl;
}
}).subscribe(new Action1<String>()
{
@Override public void call(String url)
{
Glide.with(mContext).load(url).info(ivAvatar);
}
});
//更新使用者暱稱
getUserInfo().map(new Func1<UserInfoBean,String>()
{
@Override public String call(UserInfoBean bean)
{
return bean.name;
}
}).subscribe(new Action1<String>()
{
@Override public void call(String name)
{
tvName.setText(name);
}
});
Observable getUserInfo()
{
return Observable.just(userInfo);
}
map可以把obserable傳出的資料做一個轉換再放到subscriber的onNext方法中執行~~~這樣既實現了observable的複用,又使得subcriber中的工作減輕,最重要的是提高了程式碼的逼格。
flatMap
也行你會覺得map沒啥用,甚至有些畫蛇添足,那麼看下接下來的這個進階加強版。
還是剛才的那個例子,現在每個使用者有若干好友,那麼需要在userInfo中加入一個List,這裡存放著所有好友的使用者Id
class UserInfoBean
{
public String avatarUrl;
public String name;
public String phoneNum;
public List<String> friendList;
}
現在我想獲取這個使用者所有好友的姓名,那好,那我正常情況下肯定是需要遍歷這個好友列表,獲取每個好友的id然後去查詢好友姓名吧。像這樣
UserInfoBean userInfoBean = getCurrentUser();
Observable.just(userInfoBean).subscribe(new Action1<UserInfoBean>()
{
@Override public void call(UserInfoBean bean)
{
for(String id:bean.friendList)
{
UserInfoBean friend = queryUserById(id);
System.out.print(friend.name);
}
}
});
為什麼我都用RXJava這麼洋氣的東西了,還要在響應方法中寫這麼多的程式碼呢,而且還用到了for each!好吧,解決這個問題就用到flatMap了!
先看使用flatMap改寫的程式碼!
Observable.just(userInfoBean).flatMap(new Func1<UserInfoBean, Observable<String>>() {
@Override
public Observable<String> call(UserInfoBean userInfoBean)
{
List<String> names = new ArrayList<String>();
for(String id:userInfoBean.friendList)
{
UserInfoBean friend = queryUserById(id);
names.add(friend.name);
}
return Observable.from(names);
}
}).subscribe(new Action1<String>() {
@Override
public void call(String name) {
System.out.print(name);
}
});
在flatMap的參入中放入一個Func1物件!這個Fucn1物件封裝了個有一個入參並有返回值call方法。之前還有個Action封裝的是不同入參但沒有返回值的call方法,現在對比下,顧名思義,func作為一個方法,需要有返回值,所以funcX都是封裝了不同數量入參且有一個返回值的方法。而Action作為一個動作,不需要有反饋,所以ActionX封裝的是不同數量入參且沒有返回值的方法。
接著看flatMap中func1的call方法做了些什麼。在call方法中遍歷了所有好友的id,然後根據Id查詢出好友的姓名,然後通過from將姓名返回,這樣就可以通過from來實現了1對多的一個轉換,而且把所有耗時的邏輯操作都封裝到非同步方法中執行,使得subsciber只去響應。
從這可以看出,flatMap中存放的是一個轉換的操作,但是這個轉換的操作是在什麼時候執行呼叫的呢?而且func1中的call返回的為什麼是一個observbale物件?這塊兒我覺得理解起來還是有一定難度的,至少對於我來講,耗費了不少的時間來理解。
這地方建議參考扔物線的文章(最上面的連結)並結合原始碼來看,我在這邊簡單的把我個人的理解記錄下。
首先,點開flatMap的原始碼:
public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func) {
if (getClass() == ScalarSynchronousObservable.class) {
return ((ScalarSynchronousObservable<T>)this).scalarFlatMap(func);
}
return merge(map(func));
}
忽略同步的那個判斷,直接看return,發現這裡有兩個方法,分別是merge和map,點開map:
public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
return lift(new OperatorMap<T, R>(func));
}
這裡注意兩點:1.利用func建立了一個OperatorMap(一個實現了Operator介面的類)。2.將這個OperatorMap放入List中。看到Operator了吧,這下和我們這篇文章的題目貼切了吧~~~ 而且List這個東西這的是整個轉換操作符的精華,這裡的程式碼很少,但是很重要:
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return new Observable<R>(new OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> o) {
try {
Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
// new Subscriber created and being subscribed with so 'onStart' it
st.onStart();
onSubscribe.call(st);
} catch (Throwable e) {
// localized capture of errors rather than it skipping all operators
// and ending up in the try/catch of the subscribe method which then
// prevents onErrorResumeNext and other similar approaches to error handling
Exceptions.throwIfFatal(e);
st.onError(e);
}
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
// if the lift function failed all we can do is pass the error to the final Subscriber
// as we don't have the operator available to us
o.onError(e);
}
}
});
}
可以看到list傳入了一個operator物件,返回了一個Observable物件。我們上面使用flatMap的例子中,我們自己定義了一個輸出某個使用者資訊的Observable為ObservableA,列印這個使用者所有好友姓名的subscriber為subscriberB。那麼設這個List中生成的Observable物件為ObserableB,看下這個ObservableB中的call方法,這個call方法中利用傳入的operator和原始的subsriberA生成了一個新的subscriber物件為subsriberB,然後呼叫OnSubsribe.call來註冊這個新的subsriberB。Ok,那麼看一下這個subseriberB都做了什麼,這就要先看下OperatorMap這個類中做了什麼。
public final class OperatorMap<T, R> implements Operator<R, T> {
final Func1<? super T, ? extends R> transformer;
public OperatorMap(Func1<? super T, ? extends R> transformer) {
this.transformer = transformer;
}
@Override
public Subscriber<? super T> call(final Subscriber<? super R> o) {
return new Subscriber<T>(o) {
@Override
public void onCompleted() {
o.onCompleted();
}
@Override
public void onError(Throwable e) {
o.onError(e);
}
@Override
public void onNext(T t) {
try {
o.onNext(transformer.call(t));
} catch (Throwable e) {
Exceptions.throwOrReport(e, this, t);
}
}
};
}
}
這個構造方法中的入參就是我們在flatMap中定義的func1,而call方法的入參就是我們的subsriberA,會發現在這裡call方法中,新建了一個subsriber,這個subsriber就是subsriberB,而這個subsriberB其實就是對subsriberA進行了一個帶來,其中OnComplete和OnError都是直接呼叫A的,只有在onNext中做了個轉換,而這個轉換的方法就是func1中的call方法。也就是說,現在其實是subscriberB訂閱了observbaleA,而subcriberB在獲取了ObservableA的處理結果後,經過轉換,又傳送給了ObserableA。而此時,subsriberA訂閱的就不是ObserableA,而是ObserableB了,也就是說B在A之間做了一個代理的作用。