RxJava
RxJava
Github的wiki的介紹:
RxJava
is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.
大概意思是RxJava
是Java VM
上一個(gè)使用可觀測(cè)序列來(lái)組成的一個(gè)異步的迅脐、基于事件的庫(kù)暴心。
從這么一句話,看來(lái)有兩個(gè)關(guān)鍵點(diǎn): ① 異步 ② 事件 ③可觀測(cè) 抑党。 概括性的話總是那么抽象,看了這句話,還是不清楚,RxJava
到底是干什么的激蹲。一個(gè)亙古不變的道理, 實(shí)踐出真理坷澡。只有你用了托呕,你才知道它是什么。
在我看來(lái)频敛,RxJava
就是一個(gè)異步的擴(kuò)展的觀察者模式项郊。區(qū)別于AsynTask
以及Handler的優(yōu)點(diǎn)在于代碼簡(jiǎn)潔,鏈?zhǔn)秸{(diào)用斟赚。但是注意啦着降,我這里說(shuō)的代碼簡(jiǎn)潔,可不是說(shuō)它的代碼量少拗军,而是因?yàn)樗壿嬊逦?jiǎn)潔任洞,當(dāng)你回顧代碼的時(shí)候,可以方便的看出當(dāng)初寫的邏輯发侵。而不像Handler交掏,要找到發(fā)送message的地方,以及接受message的地方, 更有各種縮進(jìn)讓你一番好找刃鳄,不方便代碼的回溯盅弛。而RxJava
的鏈?zhǔn)秸{(diào)用,使整體代碼結(jié)構(gòu)看起來(lái)很清晰。
RxJava
的神奇之處不僅在于異步的觀察者模式挪鹏,更強(qiáng)大的在于它的操作符见秽,它提供了大量的操作符來(lái)幫助你更好的解決復(fù)雜的邏輯。結(jié)合Retrofit
可以很方便的嵌套請(qǐng)求網(wǎng)絡(luò)讨盒,解決類似當(dāng)你要獲取服務(wù)器上面的數(shù)據(jù)的時(shí)候解取,首先要登錄,而登錄之前要先獲取服務(wù)器端的一個(gè)認(rèn)證挑戰(zhàn)字返顺。代碼如下:
InnerServerApi.requestLoginCode(mLoginCodeUrl, mAccount).subscribeOn(Schedulers.io()).observeOn(Schedulers.io())
.flatMap(new Func1<LoginCode, Observable<AAAData>>() {
@Override
public Observable<AAAData> call(final LoginCode loginCode) {
Log.d(TAG, loginCode.getLoginCode() + "HAHAHA");
return InnerServerApi.requestLoginData(mAAALoginUrl, loginCode.getLoginCode(), mAccount, mPassword);
}
}).subscribeOn(Schedulers.io()).subscribe(new Subscriber<AAAData>() {
@Override
public void onCompleted() {
Logger.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "login:onFailure");
}
@Override
public void onNext(AAAData loginData) {
Log.d(TAG, "login:onSuccess:");
}
});
我們不說(shuō)代碼量是否多了禀苦,反正邏輯是清晰了,這才是我們追求的创南。 如果采用AsyncTask
避免不了的CallBack嵌套伦忠,嵌套到回溯起來(lái),自己一時(shí)半會(huì)都反應(yīng)不過(guò)來(lái)呢稿辙。 這就是RxJava迷人地方之一,也是最迷人的操作符气忠。下面邻储,我會(huì)講一些比較通用的操作符。更多操作符也可以查看API,里面有圖片為你展示事件序列經(jīng)過(guò)操作符之后的流向旧噪。
下面就開(kāi)始學(xué)習(xí)基本概念啦吨娜。。淘钟。
RxJava的四個(gè)關(guān)鍵
-
Observable
被觀察者宦赠,事件的發(fā)出者。 -
Observer
觀察者米母,被動(dòng)觀察事件的發(fā)出并做相應(yīng)的處理勾扭。 通常我們都是用Subscriber
,Subscriber
是繼承自Observer
的一個(gè)類铁瞒,其實(shí)就算你實(shí)例化的是一個(gè)Observer對(duì)象妙色,底層也是用Subscriber包裹了這個(gè)Observer對(duì)象來(lái)實(shí)現(xiàn)相應(yīng)的邏輯的,所以可以直接使用Subscriber
慧耍。 - event 事件 被觀察者發(fā)出的事件
- 訂閱 (
Subscribe
)身辨,觀察者訂閱被觀察者,即觀察者監(jiān)聽(tīng)被觀察者的動(dòng)態(tài),一旦被觀察者發(fā)出了什么動(dòng)作就傳送到了觀察者那里,舉個(gè)例子锭弊,就像我們關(guān)注某個(gè)微信公眾號(hào)晨汹,關(guān)注這個(gè)動(dòng)作就相當(dāng)于 訂閱, 然后微信公眾號(hào)有新的消息推送对竣,我們就可以看到。我們就是觀察者颜矿,公眾號(hào)就是被我們觀察的對(duì)象洗贰。
通過(guò)這四個(gè)關(guān)鍵找岖,更加說(shuō)明了它是一個(gè)異步的擴(kuò)展的觀察者模式。
RxJava 的家長(zhǎng)里短
RxJava的功能強(qiáng)大在于通過(guò)操作符變換敛滋,線程調(diào)度靈活的實(shí)現(xiàn)各種事件序列的處理许布,我覺(jué)得Observable的每一個(gè)方法都可以描述成一個(gè)操作符,下面就用操作符來(lái)說(shuō)绎晃。
RxJava的觀察者模式如圖:
簡(jiǎn)單使用模板
使用RxJava
基本的步驟是下面三步:
創(chuàng)建被觀察者
Observable
創(chuàng)建一個(gè)Observable的方法有很多個(gè)蜜唾。其中一個(gè)就是create,有關(guān)于更多的創(chuàng)建操作符庶艾,創(chuàng)建型操作符 點(diǎn)這個(gè)可以了解到袁余。
Observable observable = Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
try {
subscriber.onNext(3);
subscriber.onNext(4);
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
})
call
方法里面是被觀察者訂閱之后要向觀察者發(fā)出的事件咱揍。
創(chuàng)建觀察者
Subscriber subscriber = new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.toString());
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, integer + "");
}
}
onCompleted
是整個(gè)事件序列結(jié)束之后調(diào)用的方法颖榜。在一個(gè)正確運(yùn)行的事件序列中,onCompleted()
和 onError()
有且只有一個(gè),并且是事件序列中的最后一個(gè)煤裙。需要注意的是掩完,onCompleted()
和 onError()
二者也是互斥的,即在隊(duì)列中調(diào)用了其中一個(gè)硼砰,就不應(yīng)該再調(diào)用另一個(gè),在自己創(chuàng)建的Observable里面應(yīng)該注意控制且蓬。
onError
是當(dāng)事件隊(duì)列異常。在事件處理過(guò)程中出異常時(shí)题翰,onError()
會(huì)被觸發(fā)恶阴,同時(shí)隊(duì)列自動(dòng)終止,不允許再有事件發(fā)出豹障。
onNext
,相當(dāng)于監(jiān)聽(tīng)按鈕點(diǎn)擊事件里面的onClick
冯事,即接收到被觀察者發(fā)出的事件時(shí),觀察者做處理的函數(shù)沼填。
其實(shí)還有一個(gè)onStart()
方法,不過(guò)這個(gè)是Subscriber中新添加的桅咆,Observer里面是沒(méi)有這個(gè)方法的。在 subscribe
剛開(kāi)始坞笙,而事件還未發(fā)送之前被調(diào)用岩饼,可以用于做一些準(zhǔn)備工作,例如數(shù)據(jù)的清零或重置薛夜。這是一個(gè)可選方法籍茧,默認(rèn)情況下它的實(shí)現(xiàn)為空。需要注意的是梯澜,如果對(duì)準(zhǔn)備工作的線程有要求(例如彈出一個(gè)顯示進(jìn)度的對(duì)話框寞冯,這必須在主線程執(zhí)行), onStart()
就不適用了,因?yàn)樗偸窃?subscribe
所發(fā)生的線程被調(diào)用吮龄,而不能指定線程俭茧。要在指定的線程來(lái)做準(zhǔn)備工作,可以使用 doOnSubscribe()
方法
訂閱
//被觀察者漓帚,主動(dòng)去訂閱觀察者,為了實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用就設(shè)計(jì)成觀察者主動(dòng)訂閱觀察者母债。
observeable.subscribe(subscriber);
這樣就完成了一個(gè)事件的訂閱的流程。
線程控制Scheduler
在RxJava中我們可以通過(guò)Scheduler
來(lái)實(shí)現(xiàn)多線程尝抖,指定事件發(fā)送的線程毡们,以及事件處理的線程。事件發(fā)送就是Observable
的call方法執(zhí)行的代碼昧辽,通過(guò)subscribeOn
我們可以指定call
方法執(zhí)行的線程衙熔。事件處理即Subscriber
事件處理方法執(zhí)行的線程,通過(guò)observerOn可以指定事件處理的線程搅荞。不過(guò)調(diào)用subscribeOn
或 observerOn
切換線程红氯,沒(méi)有再做切換,則此次事件一直處于最后一次切換的線程咕痛。
需要注意的是脖隶,①observerOn
可以多次調(diào)用并且有效,但SchedulerOn 雖然也可以多次調(diào)用暇检,但是只有第一次是有效的。②默認(rèn)的Observable
的產(chǎn)生事件婉称,以及通知事件給Observer
是在同一個(gè)線程里面的块仆,這個(gè)線程就是subscribe
所在的線程。③有些操作符是有默認(rèn)的調(diào)度器的王暗,可以查看官方文檔悔据。比如timer
默認(rèn)就是computation Scheduler
.
RxJava內(nèi)置Scheduler
-
Schedulers.immediate()
: 直接在當(dāng)前線程運(yùn)行,相當(dāng)于不指定線程俗壹。這是默認(rèn)的 Scheduler科汗。 -
Schedulers.newThread()
: 總是啟用新線程,并在新線程執(zhí)行操作绷雏。 -
Schedulers.io()
: I/O 操作(讀寫文件头滔、讀寫數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler涎显。行為模式和 newThread() 差不多坤检,區(qū)別在于 io() 的內(nèi)部實(shí)現(xiàn)是是用一個(gè)無(wú)數(shù)量上限的線程池,可以重用空閑的線程期吓,因此多數(shù)情況下 io() 比 newThread() 更有效率早歇。不要把計(jì)算工作放在 io() 中,可以避免創(chuàng)建不必要的線程。 -
Schedulers.computation()
: 計(jì)算所使用的 Scheduler箭跳。這個(gè)計(jì)算指的是 CPU 密集型計(jì)算晨另,即不會(huì)被 I/O 等操作限制性能的操作,例如圖形的計(jì)算谱姓。這個(gè) Scheduler 使用的固定的線程池借尿,大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation() 中逝段,否則 I/O 操作的等待時(shí)間會(huì)浪費(fèi) CPU垛玻。 -
AndroidSchedulers.mainThread()
,它指定的操作將在 Android 主線程運(yùn)行,如果希望Observer
的事件處理發(fā)生在主線程的話奶躯,就要調(diào)用observerOn(AndroidSchedulers.mainThread()).
常用的幾個(gè)操作符
-
創(chuàng)建型操作符
from
github介紹:
from() — convert an Iterable, a Future, or an Array into an Observable
from(java.lang.Iterable<? extends T> iterable) /from(T[] array),Observable
可用于將傳入的數(shù)組或者集合中拆分成具體的對(duì)象帚桩,分發(fā)下去。
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(0);
list.add(4);
Observable.from(list); //觀察者依次接收到 1,2,0,4.
just
just() — convert an object or several objects into an Observable that emits that object or those objects
just(T t1, T t2··· T tn)嘹黔,其實(shí)just與from 差不多账嚎,也是將t1, t2··· tn依次分發(fā)下去。
Observable.just("Tom" , "John", "Mary");
timer
timer() — create an Observable that emits a single item after a given delay
常用的timer(long delay, java.util.concurrent.TimeUnit unit)
, delay
延遲的時(shí)間儡蔓,unit
延遲的時(shí)間的單位郭蕉。用于定時(shí)在一定時(shí)間之后,再分發(fā)事件喂江,觀察者接收到信號(hào)召锈,做相應(yīng)處理。相當(dāng)于Handler里面的延遲發(fā)送获询。
Observable.timer(1000, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Long aLong) {
mThread = Thread.currentThread();
Log.i(TAG, "name2" +mThread.getName() + "pid" + mThread.getId());
}
});
interval
interval( ) — create an Observable that emits a sequence of integers spaced by a given time interval
interval(long interval, java.util.concurrent.TimeUnit unit)
涨岁,相當(dāng)于Timer定時(shí)器,定時(shí)分發(fā)事件吉嚣。與timer
的區(qū)別在于可以定時(shí)的重復(fù)分發(fā)事件梢薪。而timer只操作一次。repeatWhen
跟interval的作用是一樣的尝哆。
- 過(guò)濾型操作符
filter
filter() — filter items emitted by an Observable
可以過(guò)濾事件秉撇,只有符合條件的事件才能被繼續(xù)分發(fā)下去。
Observable.just(1,2,0,3).filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
//過(guò)濾掉等于0的事件
if(integer != 0) {
return integer;
}
return false;
}
})
last
last() — emit only the last item emitted by an Observable
只能發(fā)整個(gè)事件序列的最后一個(gè)事件秋泄。同理琐馆,first(),只分發(fā)整個(gè)事件序列的第一個(gè)事件印衔。
-
強(qiáng)大的轉(zhuǎn)換操作符
RxJava
中最強(qiáng)大的就是轉(zhuǎn)換型操作符了啡捶,可以對(duì)Observable進(jìn)行一些轉(zhuǎn)換,做更多的操作奸焙,實(shí)現(xiàn)嵌套之類的邏輯瞎暑。在我看來(lái)轉(zhuǎn)換操作符的作用就是將一個(gè)Observable
轉(zhuǎn)換成另外一個(gè)Observable
,我們這里假設(shè)只有兩個(gè)Observable
彤敛,當(dāng)訂閱之后,第一個(gè)Observable
的call方法首先被調(diào)用了赌,即第一個(gè)Observable
發(fā)送事件序列墨榄,第二個(gè)Observable
對(duì)第一個(gè)Observable發(fā)送出來(lái)的事件做處理(比如filiter對(duì)事件進(jìn)行過(guò)濾)或者對(duì)第一個(gè)Observable
返回的數(shù)據(jù)類型進(jìn)行處理轉(zhuǎn)換成另外一個(gè)對(duì)象或者Observable
(比如Map以及flatMap)然后第二個(gè)Observable
開(kāi)始發(fā)送事件序列,最后在Subscribe
里面進(jìn)行處理勿她。在官方的API里面有形象的"彈珠圖"來(lái)演示事件發(fā)送的發(fā)展袄秩。可以點(diǎn)這里去看 ===> RxJava API
map
map
將一個(gè)對(duì)象轉(zhuǎn)換成另外一個(gè)對(duì)象分發(fā)下去,返回的是一個(gè)Observable對(duì)象逢并。
Observable.just(1,2,3,4).map(new Func1<Integer, String>() {
@Override
public String call(Integer i) {
Log.i(TAG, "integer" +i);
return s;
}
});
上述代碼就是將Integer轉(zhuǎn)換成String對(duì)象分發(fā)下去之剧。
flatMap
flatMap
可以網(wǎng)絡(luò)請(qǐng)求的嵌套,比如請(qǐng)求服務(wù)器要先申請(qǐng)到挑戰(zhàn)字 再登錄服務(wù)器砍聊,代碼就可以如下使用:
InnerServerApi.requestLoginCode(mLoginCodeUrl, getSN()).subscribeOn(Schedulers.io()).observeOn(Schedulers.io())
.flatMap(new Func1<LoginCode, Observable<AAAData>>() {
@Override
public Observable<AAAData> call(LoginCode loginCode) {
Log.d(TAG, loginCode.getLoginCode());
String displayId = android.os.Build.DISPLAY;
return InnerServerApi.requestLoginData(mAAALoginUrl,loginCode.getLoginCode(), "doris", mPassword);
}
// if error, retry ,3 times.
}).subscribeOn(Schedulers.io()).retry(3).subscribe(new Subscriber<AAAData>() {
@Override
public void onCompleted() {
Logger.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.toString());
Log.i(TAG, "login error");
}
@Override
public void onNext(AAAData loginData) {
Log.i(TAG, "login Success");
}
});
map
與 flatMap
的區(qū)別在于map
是一對(duì)一背稼,而且map是直接返回一個(gè)對(duì)象,而flatMap
則是返回Observable
玻蝌,用于分發(fā)事件蟹肘。flatMap
的一對(duì)多的體現(xiàn)看下面這段代碼:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
這段代碼,先通過(guò)第一個(gè)Observable
把一個(gè)個(gè)目錄發(fā)送出去俯树,然后通過(guò)flatMap
再把目錄中的文件一個(gè)個(gè)分發(fā)下去帘腹。這是map做不到的。
compose
對(duì)Observable
進(jìn)行一個(gè)整體的變化许饿,flatMap
只是對(duì)接收到的事件一個(gè)一個(gè)的轉(zhuǎn)換阳欲,而compose
是對(duì)整個(gè)Observable
做一些處理。
//自定義一個(gè)轉(zhuǎn)換器
final Observable.Transformer schedulersTransformer = new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
//這樣就對(duì)observable進(jìn)行了上面call方法里面的操作陋率。
observable.compose(schedulersTransformer);
- 還有很多
zipWith
胸完,merge
等這些合并Observable
,可以看這里Combing操作符
-
錯(cuò)誤處理操作符
retry
retry(long count)
常用的retry方法翘贮,當(dāng)發(fā)生錯(cuò)誤時(shí),retry count次爆惧,比如我們網(wǎng)絡(luò)請(qǐng)求的時(shí)候失敗了狸页,我們可以重新請(qǐng)求三次,retry(3).
onErrorResumeNext
當(dāng)發(fā)生錯(cuò)誤的時(shí)候扯再,調(diào)用這個(gè)操作符里面的方法芍耘,將當(dāng)前的Observable
轉(zhuǎn)換成另一個(gè)Observable
繼續(xù)發(fā)送事件。應(yīng)用場(chǎng)景熄阻,當(dāng)我們請(qǐng)求當(dāng)前服務(wù)器失敗的時(shí)候斋竞,可以選擇一個(gè)備用的服務(wù)器地址重新請(qǐng)求數(shù)據(jù)。
RxJava 與 Retrofit 結(jié)合實(shí)例
Retrofit
與RxJava
結(jié)合可以更好的完成網(wǎng)絡(luò)請(qǐng)求秃殉。不了解Retrofit
的坝初,趕緊去學(xué)習(xí)吧浸剩,Retrofit
的底層使用OkHttp
完成網(wǎng)絡(luò)請(qǐng)求。下面給大家講講鳄袍,我用到的網(wǎng)絡(luò)請(qǐng)求的例子以及踩過(guò)的坑绢要。
-
從服務(wù)器獲取文件,失敗的話重試
/** downloadFile是返回的是一個(gè)Observable對(duì)象,picUrl是要下載的圖片url拗小,我將這個(gè)事件發(fā)送在IO線程中執(zhí)行重罪。
retry(3),如果失敗的話哀九,重試三次剿配,下載文件
map 將得到的數(shù)據(jù)寫入到文件中。
**/
downloadFile(picurl).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).retry(3).filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String picUrl) {
//調(diào)用filter阅束,過(guò)濾掉null的Url呼胚,如果picUrl是空的話,那么就不下載
return !TextUtils.isEmpty(picUrl) && !picUrl.equals("null");
}
}).map(new Func1<ResponseBody, String>() {
@Override
public String call(ResponseBody responseBody) {
return writeToFile(responseBody, mFileName);
}
}).subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
Logger.i(TAG, "testAAA downloadCompleted");
}
@Override
public void onError(Throwable e) {
Logger.i(TAG, "testAAA downloadFail" + e.toString());
}
@Override
public void onNext(String s) {
//返回 下載的文件存放路徑
Logger.i(TAG, "onNext" + s);
}
});
在上述代碼中围俘,通過(guò)filter
過(guò)濾掉了不合理的url
,其實(shí)也可以添加一個(gè)判斷是否是一個(gè)Http網(wǎng)址的判斷砸讳,你可以自己試試。并且通過(guò)map
直接將下載到的數(shù)據(jù)界牡,存儲(chǔ)到了文件當(dāng)中簿寂,而且如果下載過(guò)程中出現(xiàn)類似的 socket timeout
錯(cuò)誤,可以通過(guò)宿亡,retry
重新請(qǐng)求常遂,一個(gè)簡(jiǎn)單的鏈?zhǔn)秸{(diào)用就下載文件并保存的邏輯寫好了。 So easy挽荠! 必須給RxJava
怒贊?烁臁!圈匆!
-
嵌套網(wǎng)絡(luò)請(qǐng)求
InnerServerApi.requestLoginCode(mLoginCodeUrl, mAccount).subscribeOn(Schedulers.io()).observeOn(Schedulers.io())
.flatMap(new Func1<LoginCode, Observable<AAAData>>() {
@Override
public Observable<AAAData> call(final LoginCode loginCode) {
Log.d(TAG, loginCode.getLoginCode() + "HAHAHA");
return InnerServerApi.requestLoginData(mAAALoginUrl, loginCode.getLoginCode(), mAccount, mPassword);
}
}).subscribeOn(Schedulers.io()).subscribe(new Subscriber<AAAData>() {
@Override
public void onCompleted() {
Logger.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "login:onFailure");
}
@Override
public void onNext(AAAData loginData) {
Log.d(TAG, "login:onSuccess:");
}
});
解釋一下漠另,requestLoginCode
是通過(guò)Retrofit
定義的一個(gè)返回Observable
對(duì)象的一個(gè)方法,獲取我要登錄服務(wù)器需要用到的 驗(yàn)證字段跃赚, 得到驗(yàn)證字段之后笆搓,通過(guò)flatMap
直接通過(guò)得到的LoginCode
調(diào)用登錄方法requestLoginData
,requestLoginData
返回的也是一個(gè)Observable對(duì)象纬傲,然后訂閱满败,最后獲取到登錄返回的數(shù)據(jù)。這樣說(shuō)可能大家不明白叹括,我說(shuō)說(shuō)我這個(gè)代碼的應(yīng)用場(chǎng)景算墨,你要登錄一個(gè)服務(wù)器,首先你要向他申請(qǐng)一個(gè)跟你的賬號(hào)相關(guān)的 驗(yàn)證碼汁雷,然后再通過(guò)驗(yàn)證碼净嘀,以及賬號(hào)密碼登錄报咳。 就想你要吃蘋果,那么你要先買個(gè)水果刀面粮,然后才能切水果最后吃到水果少孝。 大家可以回想一下,沒(méi)有RxJava
之前熬苍,利用AsyncTask
我們都是如何完成這個(gè)邏輯的稍走,首先獲取到LoginCode,然后再Callback的onSuccess
方法中再調(diào)用一個(gè)AsyncTask
請(qǐng)求柴底,然后就出現(xiàn)了很多迷之縮進(jìn)婿脸,相比之下,RxJava
簡(jiǎn)直是飛流直下柄驻,邏輯明了清晰狐树。不相信的機(jī)智的你,可以寫一段對(duì)比一下鸿脓。 我這里就不貼出來(lái)的抑钟。
-
錯(cuò)誤處理 onErrorResumeNext
在應(yīng)用開(kāi)發(fā)中,會(huì)遇到這樣的需求野哭,有幾個(gè)備份的網(wǎng)絡(luò)地址在塔,當(dāng)你請(qǐng)求第一個(gè)網(wǎng)絡(luò)地址不成功的時(shí)候,你想要用備份的網(wǎng)絡(luò)地址拨黔。RxJava
可以很方便的蛔溃,幫你捕捉到錯(cuò)誤,并且用備份的地址篱蝇,重新請(qǐng)求贺待。 就獲取網(wǎng)絡(luò)時(shí)間的代碼,來(lái)舉個(gè)例子吧零截。
Observable.create(new Observable.OnSubscribe<Date>() {
@Override
public void call(Subscriber<? super Date> subscriber) {
try {
//第一次從百度上面獲取網(wǎng)絡(luò)時(shí)間
mTimeUrl = "http://www.baidu.com/";
Logger.d(TAG, "mTimeUrl" + mTimeUrl);
Date date = GetNetworkTime.getWebsiteDate();
Logger.d(TAG, "call date" + date);
subscriber.onNext(date);
subscriber.onCompleted();
} catch (IOException e) {
//如果網(wǎng)絡(luò)超時(shí)則調(diào)用onError麸塞,觸發(fā)onErrorResumeNext通過(guò)備份的地址 http://www.beijing-time.org 獲取網(wǎng)絡(luò)時(shí)間。
subscriber.onError(e);
e.printStackTrace();
}
}
}).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).onErrorResumeNext(new Func1<Throwable, Observable<? extends Date>>() {
@Override
public Observable<? extends Date> call(Throwable throwable) {
return Observable.create(new Observable.OnSubscribe<Date>() {
@Override
public void call(Subscriber<? super Date> subscriber) {
//通過(guò)http://www.beijing-time.org 請(qǐng)求時(shí)間涧衙,如果這次訪問(wèn)失敗喘垂,則獲取網(wǎng)絡(luò)時(shí)間失敗
mTimeUrl = "http://www.beijing-time.org";
try {
Date date = GetNetworkTime.getWebsiteDate();
Logger.d(TAG, "call1" + date);
subscriber.onNext(date);
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
}
}).subscribe(new Observer<Date>() {
@Override
public void onCompleted() {
Logger.d(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Date date) {
Logger.d(TAG, "call3" + date);
netWorkCallback.doSometing(date);
}
});
在上述代碼中,當(dāng)?shù)谝粋€(gè)Observable
發(fā)送獲取網(wǎng)絡(luò)時(shí)間的時(shí)候超時(shí)的時(shí)候绍撞,我們自己調(diào)用OnError
方法,從而onErrorResumeNext
攔截到錯(cuò)誤并且將當(dāng)前的Observable
轉(zhuǎn)換成另外一個(gè)Observable
得院,通過(guò)這個(gè)新的Observable
繼續(xù)發(fā)送新的事件傻铣,這里的新的事件就是通過(guò)備份的url,再次獲取網(wǎng)絡(luò)是事件祥绞。這個(gè)就可以完成多個(gè)地址切換請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)非洲,當(dāng)?shù)谝粋€(gè)地址不成功鸭限,換第二個(gè)地址。這在做應(yīng)用開(kāi)發(fā)時(shí)也經(jīng)常用到两踏。我覺(jué)得挺實(shí)用的败京,當(dāng)然onErrorResumeNext
的應(yīng)用場(chǎng)景還有很多,就等你慢慢發(fā)掘吧梦染。
總結(jié)
在用RxJava的時(shí)候赡麦,要注意當(dāng)前的操作是哪個(gè)線程。注意不要把應(yīng)該在UI線程操作的放在了子線程帕识,也不要把大量的操作放在主線程泛粹。
在
Observable
發(fā)送一串事件序列的時(shí)候,如果其中有一個(gè)出錯(cuò)了肮疗,那么接下來(lái)的事件晶姊,觀察者都不會(huì)接收到。即OnError
是整個(gè)事件結(jié)束的標(biāo)志伪货,如果出錯(cuò)了们衙,并且沒(méi)有做什么出錯(cuò)處理,那么就直接調(diào)用了OnError
碱呼,結(jié)束整個(gè)事件蒙挑。 所以這意味著,你利用RxJava
發(fā)送了一個(gè)下載十個(gè)文件的事件序列時(shí)巍举,如果其中有一個(gè)文件下載失敗脆荷,其他的文件就停止下載了。這個(gè)時(shí)候你只能用懊悯,for循環(huán)蜓谋,然后一個(gè)一個(gè)下載,方便控制炭分。
參考鏈接
最后的最后桃焕,我說(shuō)的哪里不對(duì)的或者有什么問(wèn)題,歡迎留言捧毛,大家共進(jìn)步观堂。O