大話RxJava 的基本及應(yīng)用

RxJava

RxJavaGithub的wiki的介紹:

RxJavais a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.

大概意思是RxJavaJava 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)鍵

  1. Observable 被觀察者宦赠,事件的發(fā)出者。
  2. Observer 觀察者米母,被動(dòng)觀察事件的發(fā)出并做相應(yīng)的處理勾扭。 通常我們都是用SubscriberSubscriber是繼承自Observer的一個(gè)類铁瞒,其實(shí)就算你實(shí)例化的是一個(gè)Observer對(duì)象妙色,底層也是用Subscriber包裹了這個(gè)Observer對(duì)象來(lái)實(shí)現(xiàn)相應(yīng)的邏輯的,所以可以直接使用Subscriber慧耍。
  3. event 事件 被觀察者發(fā)出的事件
  4. 訂閱 (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的觀察者模式如圖:

RxJava1.png

簡(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)用subscribeOnobserverOn切換線程红氯,沒(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

Paste_Image.png

常用的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

Paste_Image.png

可以過(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è)事件印衔。

Paste_Image.png
  • 強(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ì)象逢并。

Paste_Image.png
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ù)器砍聊,代碼就可以如下使用:

Paste_Image.png
 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");
            }

        });

mapflatMap 的區(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); 
  1. 還有很多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).

Paste_Image.png

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ù)。

Paste_Image.png

RxJava 與 Retrofit 結(jié)合實(shí)例

RetrofitRxJava結(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)用登錄方法requestLoginDatarequestLoginData返回的也是一個(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é)

  1. 在用RxJava的時(shí)候赡麦,要注意當(dāng)前的操作是哪個(gè)線程。注意不要把應(yīng)該在UI線程操作的放在了子線程帕识,也不要把大量的操作放在主線程泛粹。

  2. 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è)下載,方便控制炭分。

參考鏈接

  1. 給 Android 開(kāi)發(fā)者的 RxJava 詳解

  2. 官方文檔

  3. RxJava API

最后的最后桃焕,我說(shuō)的哪里不對(duì)的或者有什么問(wèn)題,歡迎留言捧毛,大家共進(jìn)步观堂。O

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呀忧,隨后出現(xiàn)的幾起案子师痕,更是在濱河造成了極大的恐慌,老刑警劉巖而账,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胰坟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡泞辐,警方通過(guò)查閱死者的電腦和手機(jī)笔横,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門竞滓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吹缔,你說(shuō)我怎么就攤上這事商佑。” “怎么了厢塘?”我有些...
    開(kāi)封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵茶没,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我俗冻,道長(zhǎng)礁叔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任迄薄,我火速辦了婚禮琅关,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讥蔽。我一直安慰自己涣易,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布冶伞。 她就那樣靜靜地躺著新症,像睡著了一般。 火紅的嫁衣襯著肌膚如雪响禽。 梳的紋絲不亂的頭發(fā)上徒爹,一...
    開(kāi)封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音芋类,去河邊找鬼隆嗅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛侯繁,可吹牛的內(nèi)容都是我干的胖喳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贮竟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丽焊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起咕别,我...
    開(kāi)封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤技健,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惰拱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雌贱,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帽芽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翔冀,死狀恐怖导街,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纤子,我是刑警寧澤搬瑰,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站控硼,受9級(jí)特大地震影響泽论,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卡乾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一翼悴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幔妨,春花似錦鹦赎、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锁施,卻和暖如春陪踩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悉抵。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工肩狂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人基跑。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓婚温,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親媳否。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栅螟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容