RxJava實際應(yīng)用案例

前言

上一篇文章命锄,我們已經(jīng)了解了RxJava的相關(guān)概念以及基礎(chǔ)使用方式衣洁。這篇文章將會結(jié)合RxJava知識點與開發(fā)中遇到的實際場景進(jìn)行講解,讓大家能夠通過這些例子更好的理解與掌握RxJava的用法與思想

數(shù)據(jù)請求

這是一個完整的數(shù)據(jù)請求例子,從緩存、網(wǎng)絡(luò)以及數(shù)據(jù)解析等垛吗。本例要實現(xiàn)的是若數(shù)據(jù)庫有對應(yīng)的數(shù)據(jù)則直接使用緩存的數(shù)據(jù),否則再通過網(wǎng)絡(luò)請求獲取數(shù)據(jù)抱既,并且完成解析與緩存等操作

1.創(chuàng)建網(wǎng)絡(luò)請求Observable從網(wǎng)絡(luò)獲取數(shù)據(jù)
(1)Observable.create()調(diào)起網(wǎng)絡(luò)請求獲取數(shù)據(jù)
(2)map操作符將網(wǎng)絡(luò)響應(yīng)映射為數(shù)據(jù)實體
(3)doOnNext操作符將數(shù)據(jù)存儲到數(shù)據(jù)庫
2.創(chuàng)建數(shù)據(jù)庫緩存讀取Observable讀取緩存
3.Observable.concat合并上述兩個Observable

        Observable net = Observable.create(new ObservableOnSubscribe<Response>() {
            @Override
            public void subscribe(ObservableEmitter<Response> e) throws Exception {
                Request.Builder builder = new Request.Builder()
                        .url("http://gank.io/api/data/Android/10/1")
                        .get();
                Request request = builder.build();
                Call call = new OkHttpClient().newCall(request);
                Response response = call.execute();
                e.onNext(response);
            }
        }).map(new Function<Response, GankIOAndroidEntity>() {
            @Override
            public GankIOAndroidEntity apply(Response response) throws Exception {
                ResponseBody body = response.body();
                if(body!=null){
                    return JSON.parseObject(body.toString(),GankIOAndroidEntity.class);
                }
                return null;
            }
        }).doOnNext(new Consumer<GankIOAndroidEntity>() {
            @Override
            public void accept(GankIOAndroidEntity gankIOAndroidEntity) throws Exception {
                saveDB();
            }
        });

        Observable cache = Observable.create(new ObservableOnSubscribe<GankIOAndroidEntity>() {
            @Override
            public void subscribe(ObservableEmitter<GankIOAndroidEntity> e) throws Exception {
                GankIOAndroidEntity entity = getFromDB();
                if(entity!=null){
                    e.onNext(entity);
                }else{
                    e.onComplete();
                }
            }
        });

        Observable.concat(cache,net)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<GankIOAndroidEntity>() {
                    @Override
                    public void accept(GankIOAndroidEntity gankIOAndroidEntity) throws Exception {
                        displayUI();
                    }
                });

當(dāng)然還有一種情況职烧,就是進(jìn)入頁面時若有緩存先展示緩存,但不管是否有緩存都請求網(wǎng)絡(luò)數(shù)據(jù)并更新緩存防泵。咱們只需要修改一下cache對象即可,不管緩存查詢結(jié)果如何蝗敢,都執(zhí)行net操作

        Observable cache = Observable.create(new ObservableOnSubscribe<GankIOAndroidEntity>() {
            @Override
            public void subscribe(ObservableEmitter<GankIOAndroidEntity> e) throws Exception {
                GankIOAndroidEntity entity = getFromDB();
                if(entity!=null){
                    e.onNext(entity);
                }
                e.onComplete(); // 不管緩存查詢結(jié)果如何捷泞,都執(zhí)行net操作
            }
        });

網(wǎng)絡(luò)請求嵌套

我想大家肯定都遇到過類似這樣的情況,在某個接口請求完成后再進(jìn)行下一個請求的調(diào)用寿谴。比如:先注冊成功后再調(diào)用登陸接口锁右、先上傳頭像再保存資料、先更改數(shù)據(jù)再查詢數(shù)據(jù)等等例子

我們可以通過flatMap操作符來方便的實現(xiàn)上述的請求嵌套讶泰,擺脫天花亂墜的回調(diào)嵌套咏瑟。復(fù)習(xí)一下flatMap的作用:FlatMap將一個發(fā)射數(shù)據(jù)的Observable變換為多個Observables,然后將它們發(fā)射的數(shù)據(jù)合并后放進(jìn)一個單獨的Observable

        Observable.create(new ObservableOnSubscribe<CallBackEntity>() {
            @Override
            public void subscribe(ObservableEmitter<CallBackEntity> e) throws Exception {
                RequestBody body = new FormBody.Builder()
                        .add("username","lee")
                        .add("password","lee")
                        .build();
                Request.Builder builder = new Request.Builder()
                        .url("http://xxx/register")
                        .post(body);
                Request request = builder.build();
                Call call = new OkHttpClient().newCall(request);
                Response response = call.execute();
                CallBackEntity entity =
                        JSON.parseObject(response.body().toString(),CallBackEntity.class);
                e.onNext(entity);
            }
        }).flatMap(new Function<CallBackEntity, ObservableSource<CallBackEntity>>() {
            @Override
            public ObservableSource<CallBackEntity> apply(CallBackEntity callBackEntity) throws Exception {
                // register successful
                if(callBackEntity.getCode() == 0){
                    return Observable.create(new ObservableOnSubscribe<CallBackEntity>() {
                        @Override
                        public void subscribe(ObservableEmitter<CallBackEntity> e) throws Exception {
                            RequestBody body = new FormBody.Builder()
                                    .add("username","lee")
                                    .add("password","lee")
                                    .build();
                            Request.Builder builder = new Request.Builder()
                                    .url("http://xxx/login")
                                    .post(body);
                            Request request = builder.build();
                            Call call = new OkHttpClient().newCall(request);
                            Response response = call.execute();
                            CallBackEntity entity =
                                    JSON.parseObject(response.body().toString(),CallBackEntity.class);
                        }
                    });
                }
                return null;
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<CallBackEntity>() {
                    @Override
                    public void accept(CallBackEntity callBackEntity) throws Exception {
                        displayUI();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        // error
                    }
                });

數(shù)據(jù)合并

某個頁面的展示可能需要同時調(diào)用好多個接口來得到完整的數(shù)據(jù)痪署,可以用zip合并數(shù)據(jù)

        Observable data1 = Observable.create(subscibe1);
        Observable data2 = Observable.create(subscibe2);
        Observable.zip(data1, data2, new BiFunction() {
            @Override
            public Object apply(Data1 o, Data2 o2) throws Exception {
                Data data = new Data(o,o2);
                return data;
            }
        }).subscribe(new Consumer() {
            @Override
            public void accept(Object o) throws Exception {
                displayUI();
            }
        });

按鈕防點擊

登陸按鈕码泞、發(fā)送驗證碼按鈕等各種與接口請求或者邏輯操作關(guān)聯(lián)按鈕,防止多次點擊造成的非預(yù)期效果

        RxView.clicks(btn)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {

                    }
                });

復(fù)雜的數(shù)據(jù)變換

        Observable.just(dbData)
                .map(new Function<List<String>, Object>() {
                    @Override
                    public Object apply(List<String> strings) throws Exception {
                        return null;
                    }
                })
                .filter(new Predicate<Object>() {
                    @Override
                    public boolean test(Object o) throws Exception {
                        return false;
                    }
                }).distinct(new Function<Object, Object>() {
            @Override
            public Object apply(Object o) throws Exception {
                return null;
            }
        }).take(pageSize)
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {

                    }
                });

按鈕的增量監(jiān)聽

addTextChangedListener與setOnClickListener狼犯,相信大家都不陌生吧余寥,不知道大家知道它們倆對于事件的監(jiān)聽有什么區(qū)別嗎?不了解的同學(xué)我解釋下悯森,addTextChangedListener內(nèi)部維護(hù)了一個集合存儲設(shè)置的所有監(jiān)聽對象宋舷,當(dāng)事件產(chǎn)生時會向這所有監(jiān)聽發(fā)送回調(diào)。而setOnClickListener內(nèi)部存儲了OnClickListener對象瓢姻,用于保存設(shè)置的監(jiān)聽對象祝蝠,也就是說只有一個。結(jié)果很明顯幻碱,addTextChangedListener是增量監(jiān)聽绎狭,setOnClickListener是覆蓋

那么有沒有辦法實現(xiàn)OnClickListener的增量監(jiān)聽呢?有的~
當(dāng)然收班,類似的還有OnTouchListener坟岔、focusChanges、drags等等

        Observable<Object> shareClick = RxView.clicks(textView).share();
        Observable<MotionEvent> shareTouch = RxView.touches(textView).share();

        shareClick.subscribe(new Consumer<Object>() {
            @Override
            public void accept(Object o) throws Exception {
                // listener1
            }
        });

        shareClick.subscribe(new Consumer<Object>() {
            @Override
            public void accept(Object o) throws Exception {
                // listener2
            }
        });

獲取驗證碼倒計時

        verifyCodeObservable = RxView.clicks(mBt)
                .throttleFirst(SECOND, TimeUnit.SECONDS)
                .subscribeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Action1<Void>() {
                    @Override
                    public void call(Void aVoid) {
                        RxView.enabled(mBt).call(false);
                    }
                });

        verifyCodeObservable.subscribe(new Action1<Void>() {
                    @Override
                    public void call(Void aVoid) {
                        Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
                                .take(SECOND)
                                .subscribe(new Observer<Long>() {
                                    @Override
                                    public void onCompleted() {
                                        RxTextView.text(mBt).call("獲取驗證碼");
                                        RxView.enabled(mBt).call(true);
                                    }

                                    @Override
                                    public void onError(Throwable e) {
                                        Log.e(TAG, e.toString());
                                    }

                                    @Override
                                    public void onNext(Long aLong) {
                                        RxTextView.text(mBt).call("剩余" + (SECOND - aLong) + "秒");
                                    }
                                });
                    }
                });

必填項/表單驗證

        Observable<CharSequence> ObservableName = RxTextView.textChanges(mEtPhone);
        Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mEtPassword);

        Observable.combineLatest(ObservableName, ObservablePassword, new Func2<CharSequence, CharSequence, Boolean>() {
            @Override
            public Boolean call(CharSequence phone, CharSequence password) {
                return isPhoneValid(phone.toString()) && isPasswordValid(password.toString());
            }
        }).subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                RxView.enabled(mBtLogin).call(aBoolean);
            }
        });

        RxView.clicks(mBtLogin)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Void>() {
                    @Override
                    public void call(Void aVoid) {
                        Toast.makeText(LoginActivity.this, "登錄成功摔桦!" ,Toast.LENGTH_SHORT).show();
                    }
                });

響應(yīng)式SP

        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        RxSharedPreference rxSharedPreference = RxSharedPreference.create(preferences);
        Preferences<Boolean> choose = rxSharedPreference.getBoolean("choose",false);
        RxCompoundButton.checkedChanges(checkBox)
                .subscribe(rxSharedPreference.asAction());

RxJava各種擴(kuò)展庫

rx-preferences -使SharedPreferences支持RxJava

RxAndroid -RxJava的Android拓展

RxLifecycle -幫助使用了RxJava的安卓應(yīng)用控制生命周期

RxBinding -安卓UI控件的RxJava綁定API

storio -支持RxJava的數(shù)據(jù)庫

retrofit -支持RxJava的網(wǎng)絡(luò)請求庫

sqlbrite -支持RxJava的sqlite數(shù)據(jù)庫

RxPermissions -RxJava實現(xiàn)的Android運(yùn)行時權(quán)限控制

reark -RxJava architecture library for Android

frodo -Android Library for Logging RxJava Observables and Subscribers.

總結(jié)

沒有做不到社付,只有想不到承疲。加油~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸥咖,隨后出現(xiàn)的幾起案子燕鸽,更是在濱河造成了極大的恐慌,老刑警劉巖啼辣,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啊研,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸥拧,警方通過查閱死者的電腦和手機(jī)党远,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來富弦,“玉大人沟娱,你說我怎么就攤上這事⊥蠊瘢” “怎么了济似?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盏缤。 經(jīng)常有香客問我砰蠢,道長,這世上最難降的妖魔是什么唉铜? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任台舱,我火速辦了婚禮,結(jié)果婚禮上打毛,老公的妹妹穿的比我還像新娘柿赊。我一直安慰自己,他們只是感情好幻枉,可當(dāng)我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布碰声。 她就那樣靜靜地躺著,像睡著了一般熬甫。 火紅的嫁衣襯著肌膚如雪胰挑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天椿肩,我揣著相機(jī)與錄音瞻颂,去河邊找鬼。 笑死郑象,一個胖子當(dāng)著我的面吹牛贡这,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厂榛,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼盖矫,長吁一口氣:“原來是場噩夢啊……” “哼丽惭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辈双,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤责掏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后湃望,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體换衬,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年证芭,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞳浦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡檩帐,死狀恐怖术幔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情湃密,我是刑警寧澤,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布四敞,位于F島的核電站泛源,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏忿危。R本人自食惡果不足惜达箍,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铺厨。 院中可真熱鬧缎玫,春花似錦、人聲如沸解滓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洼裤。三九已至邻辉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腮鞍,已是汗流浹背值骇。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留移国,地道東北人吱瘩。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像迹缀,于是被迫代替她去往敵國和親使碾。 傳聞我的和親對象是個殘疾皇子蜜徽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,926評論 2 361

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