RxJava操作符在android中的使用場景詳解(一)

最近學(xué)習(xí)了RxJava在android中的使用擂涛,關(guān)于RxJava是啥羞延,為什么要用RxJava,好在哪逛裤,這里就不敘述了指孤,如果想要了解請移步官方文檔启涯、大神文章

這里只講解一下RxJava中的操作符恃轩,以及項(xiàng)目中具體的使用場景结洼。

因?yàn)閷W(xué)習(xí)了有20個(gè)操作符,可能一篇文章過于臃腫叉跛,所以打算寫成系列文章松忍,本文中所有的操作符使用,都寫在了一個(gè)demo中筷厘,已上傳至github

場景一:RxJava基本使用

配合Retrofit請求網(wǎng)絡(luò)數(shù)據(jù)鸣峭,如果你對Retrofit不熟悉就先看Retrofit官網(wǎng)宏所,實(shí)現(xiàn)步驟如下:

  1. 先是build.gradle的配置
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'
    compile 'com.jakewharton:butterknife:7.0.1'

    也就是說本文是基于RxJava1.1.0和Retrofit 2.0.0-beta3來進(jìn)行的。
    添加rxandroid是因?yàn)閞xjava中的線程問題摊溶。

  2. 基本網(wǎng)絡(luò)請求使用準(zhǔn)備

    我們使用http://zhuangbi.info/search?q=param測試連接爬骤,返回的是json格式,代碼就不貼了

    接下來我們要?jiǎng)?chuàng)建一個(gè)接口取名為ZhuangbiApi莫换,代碼如下:

    public interface ZhuangbiApi {
        @GET("search")
        Observable<List<ImageInfoBean>> search(@Query("q") String query);
    }
    
    

    Retrofit霞玄、Gson、RxJava結(jié)合使用拉岁,建立網(wǎng)絡(luò)請求類:

     public static ZhuangbiApi getZhuangbiApi() {
            if (zhuangbiApi == null) {
                Retrofit retrofit = new Retrofit.Builder()
                        .client(okHttpClient)
                        .baseUrl("http://zhuangbi.info/")
                        .addConverterFactory(gsonConverterFactory)
                        .addCallAdapterFactory(rxJavaCallAdapterFactory)
                        .build();
                zhuangbiApi = retrofit.create(ZhuangbiApi.class);
            }
            return zhuangbiApi;
        }
    
  3. 具體使用
    將要查詢的關(guān)鍵字傳進(jìn)去坷剧,使用上面建立的網(wǎng)絡(luò)請求類請求數(shù)據(jù),并在訂閱者的回調(diào)方法中膛薛,進(jìn)行網(wǎng)絡(luò)請求結(jié)果的處理

     private void search(String key) {
    
            subscription = Network.getZhuangbiApi()
                    .search(key)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(getObserver());
        }        
    
        private Observer<? super List<ImageInfoBean>> getObserver() {
    
            if (null == observer) {
                observer = new Observer<List<ImageInfoBean>>() {
                    @Override
                    public void onCompleted() {
    
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        swipeRefreshLayout.setRefreshing(false);
                        Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
                    }
    
    
                    @Override
                    public void onNext(List<ImageInfoBean> images) {
    
                        swipeRefreshLayout.setRefreshing(false);
                        adapter.setImages(images);
                    }
                };
            }
    
            return observer;
        }
    
  1. 詳解

    search方法中傳入的key是要查詢的關(guān)鍵詞听隐,getObserver()是獲取訂閱者對象补鼻,并在其回調(diào)方法中根據(jù)返回結(jié)果哄啄,做相應(yīng)處理:

    其中onNext方法返回了數(shù)據(jù),這樣我們能夠在onNext里面處理數(shù)據(jù)相關(guān)的邏輯风范;
    onError方法中處理錯(cuò)誤咨跌,同時(shí)也可以停止ProgressDialog等;
    onComplated只調(diào)用一次結(jié)束本次請求操作硼婿,也可以停止ProgressDialog锌半;

場景二:Map操作符的使用(變換)

對Observable發(fā)射的每一項(xiàng)數(shù)據(jù)應(yīng)用一個(gè)函數(shù),執(zhí)行變換為指定類型的操作寇漫,然后再發(fā)射

有些服務(wù)端的接口設(shè)計(jì)刊殉,會(huì)在返回的數(shù)據(jù)外層包裹一些額外信息,這些信息對于調(diào)試很有用州胳,但本地顯示是用不到的记焊。使用 map() 可以把外層的格式剝掉,只留下我們只關(guān)心的部分栓撞,具體實(shí)現(xiàn)步驟如下:

  1. 網(wǎng)絡(luò)請求使用準(zhǔn)備
    我們使用http://gank.io/api/測試連接
    接下來我們要?jiǎng)?chuàng)建一個(gè)接口取名為GankApi 遍膜,代碼如下:

    public interface GankApi {
        
        @GET("data/福利/{number}/{page}")
        Observable<BeautyResult> getBeauties(@Path("number") int number, @Path("page") int page);
        
    }    
    

    Retrofit、Gson瓤湘、RxJava結(jié)合使用瓢颅,建立網(wǎng)絡(luò)請求類:

     public static GankApi getGankApi() {
            if (gankApi == null) {
                Retrofit retrofit = new Retrofit.Builder()
                        .client(okHttpClient)
                        .baseUrl("http://gank.io/api/")
                        .addConverterFactory(gsonConverterFactory)
                        .addCallAdapterFactory(rxJavaCallAdapterFactory)
                        .build();
                gankApi = retrofit.create(GankApi.class);
            }
            return gankApi;
        }
    
  2. 數(shù)據(jù)轉(zhuǎn)換
    返回?cái)?shù)據(jù)就不貼了,有興趣可以請求接口看一下弛说。
    接口返回的數(shù)據(jù)包含了一些額外的信息挽懦,但是我們只需要返回?cái)?shù)據(jù)中的list部分,所以創(chuàng)建一個(gè)類木人,來實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)換的功能巾兆,代碼如下:

    public class BeautyResult2Beautise implements Func1<BeautyResult, List<ImageInfoBean>> {
    
    
        public static BeautyResult2Beautise newInstance() {
            return new BeautyResult2Beautise();
        }
    
    
        /**
         * 將接口返回的BeautyResult數(shù)據(jù)中的list部分提取出來猎物,返回集合List<ImageInfoBean>
         * @param beautyResult
         * @return
         */
        @Override
        public List<ImageInfoBean> call(BeautyResult beautyResult) {
    
            List<ImageInfoBean> imageInfoBeanList = new ArrayList<>(beautyResult.results.size());
    
            for (ImageInfoBean bean : beautyResult.results) {
                ImageInfoBean imageInfoBean = new ImageInfoBean();
    
                imageInfoBean.description = bean.desc;
    
                imageInfoBean.image_url = bean.url;
    
                imageInfoBeanList.add(imageInfoBean);
    
            }
    
            return imageInfoBeanList;
        }
    }
    
  1. 操作符的使用

    加載數(shù)據(jù)

    /**
         * 加載數(shù)據(jù)的方法
         * @param page
         */
        private void loadPage(int page) {
            mSwipeRefreshLayout.setRefreshing(true);
            unsubscribe();
            subscription = Network.getGankApi()
                    .getBeauties(8, page)
                    .map(BeautyResult2Beautise.newInstance())
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(getObserver());
        }
    

    訂閱者

    /**
         * 獲取訂閱者
         * @return
         */
        private Observer<? super List<ImageInfoBean>> getObserver() {
    
            if (null == observer) {
                observer = new Observer<List<ImageInfoBean>>() {
                    @Override
                    public void onCompleted() {
                        mSwipeRefreshLayout.setRefreshing(false);
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        mSwipeRefreshLayout.setRefreshing(false);
    
                        Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
                    }
    
                    @Override
                    public void onNext(List<ImageInfoBean> images) {
                        adapter.setImages(images);
                    }
                };
            }
    
    
            return observer;
        }
    
  1. 詳解
    Map操作符對Observable發(fā)射的每一項(xiàng)數(shù)據(jù)應(yīng)用一個(gè)函數(shù),執(zhí)行變換操作角塑,然后返回一個(gè)發(fā)射這些結(jié)果的Observable蔫磨。
    本例中,接口返回的數(shù)據(jù)格式是:
    public class BeautyResult {
    
    
        public boolean error;
    
        public List<ImageInfoBean> results;
    
    
    }
    
    
    但是我們只關(guān)心list部分的數(shù)據(jù)圃伶,所以進(jìn)行轉(zhuǎn)換操作堤如,這樣訂閱者回調(diào)方法中拿到的數(shù)據(jù)直接進(jìn)行使用就好了

場景三:Zip操作符的使用(結(jié)合)

通過一個(gè)函數(shù)將多個(gè)Observables的發(fā)射物結(jié)合到一起,基于這個(gè)函數(shù)的結(jié)果為每個(gè)結(jié)合體發(fā)射單個(gè)數(shù)據(jù)項(xiàng)窒朋,具體實(shí)現(xiàn)步驟如下:

  1. 網(wǎng)絡(luò)請求裝備
    網(wǎng)絡(luò)請求Api搀罢,以及請求類,還是使用場景一侥猩、二中的創(chuàng)建好的榔至。

  2. 請求數(shù)據(jù),并結(jié)合欺劳,代碼如下:

     /**
         * 請求兩個(gè)接口唧取,對返回的數(shù)據(jù)進(jìn)行結(jié)合
         */
        private void load() {
    
            swipeRefreshLayout.setRefreshing(true);
            subscription = Observable.zip(Network.getGankApi().getBeauties(188, 1).map(BeautyResult2Beautise.newInstance()),
                    Network.getZhuangbiApi().search("裝逼"),
                    new Func2<List<ImageInfoBean>, List<ImageInfoBean>, List<ImageInfoBean>>() {
                        @Override
                        public List<ImageInfoBean> call(List<ImageInfoBean> imageInfoBeen, List<ImageInfoBean> imageInfoBeen2) {
    
                            int num = imageInfoBeen.size() < imageInfoBeen2.size() ? imageInfoBeen.size() : imageInfoBeen2.size();
                            List<ImageInfoBean> list = new ArrayList<>();
                            for (int i = 0; i < num; i++) {
    
                                list.add(imageInfoBeen.get(i));
                                list.add(imageInfoBeen2.get(i));
    
                            }
    
                            return list;
                        }
                    }).subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(getObserver());
        }
    
  1. 詳解
    請求GankApi中的數(shù)據(jù)使用map操作符進(jìn)行轉(zhuǎn)換,取出自己想要的list數(shù)據(jù)划提,然后結(jié)合ZhuangbiApi中的數(shù)據(jù)枫弟,形成新的數(shù)據(jù)集合,填充到view鹏往。

    Zip操作符使用函數(shù)按順序結(jié)合多個(gè)Observables發(fā)射的數(shù)據(jù)項(xiàng)淡诗,然后它發(fā)射這個(gè)函數(shù)返回的結(jié)果,它只發(fā)射與數(shù)據(jù)項(xiàng)最少的那個(gè)Observable一樣多的數(shù)據(jù)伊履。

    一般app中同一個(gè)界面有時(shí)會(huì)需要同時(shí)訪問不同接口韩容,然后將結(jié)果糅合后轉(zhuǎn)為統(tǒng)一的格式后輸出(例如將第三方廣告 API 的廣告夾雜進(jìn)自家平臺(tái)返回的數(shù)據(jù) List 中)。這種并行的異步處理比較麻煩唐瀑,不過用了 zip() 之后就會(huì)簡單明了群凶。
    上一個(gè)效果圖:


    圖片描述
    圖片描述

    可以看出,recyclerView中使用了一個(gè)數(shù)據(jù)集合介褥,但左側(cè)的一列展示的是GankApi中的數(shù)據(jù)座掘,右側(cè)一列展示的是ZhuangbiApi 中的數(shù)據(jù)。

場景四:CombineLatest操作符的使用(結(jié)合)

結(jié)合多個(gè)Observable發(fā)射的最近數(shù)據(jù)項(xiàng)柔滔,當(dāng)原始Observables的任何一個(gè)發(fā)射了一條數(shù)據(jù)時(shí)溢陪,CombineLatest使用一個(gè)函數(shù)結(jié)合它們最近發(fā)射的數(shù)據(jù),然后發(fā)射這個(gè)函數(shù)的返回值睛廊,具體實(shí)現(xiàn)步驟如下:

  1. 使用場景形真,用一個(gè)簡單明了的圖片來表示吧


    圖片描述
    圖片描述
  2. 上圖簡單演示了CombineLatest的使用場景,看代碼吧:

     /**
         * 將3個(gè)EditText的事件進(jìn)行結(jié)合
         */
        private void combineLatestEvent() {
    
            usernameObservable = RxTextView.textChanges(mUsername).skip(1);
            emailObservable = RxTextView.textChanges(mEmail).skip(1);
            passwordObservable = RxTextView.textChanges(mPassword).skip(1);
    
            subscription = Observable.combineLatest(usernameObservable, emailObservable, passwordObservable,
                    new Func3<CharSequence, CharSequence, CharSequence, Boolean>() {
                        @Override
                        public Boolean call(CharSequence userName, CharSequence email, CharSequence password) {
    
                            boolean isUserNameValid = !TextUtils.isEmpty(userName) && (userName.toString().length() > 2 && userName.toString().length() < 9);
    
                            if (!isUserNameValid) {
                                mUsername.setError("用戶名無效");
                            }
    
    
                            boolean isEmailValid = !TextUtils.isEmpty(email) && Patterns.EMAIL_ADDRESS.matcher(email).matches();
    
                            if (!isEmailValid) {
                                mEmail.setError("郵箱無效");
                            }
    
                            boolean isPasswordValid = !TextUtils.isEmpty(password) && (password.toString().length() > 6 && password.toString().length() < 11);
    
                            if (!isPasswordValid) {
                                mPassword.setError("密碼無效");
                            }
    
    
                            return isUserNameValid && isEmailValid && isPasswordValid;
                        }
                    })
                    .subscribe(getObserver());
        }
    
    
    /**
         * 獲取訂閱者
         * @return
         */
        private Observer<Boolean> getObserver() {
            return new Observer<Boolean>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(Boolean aBoolean) {
                    //更改注冊按鈕是否可用的狀態(tài)
                    mButton.setEnabled(aBoolean);
                }
            };
        }
    
  3. 詳解
    CombineLatest操作符行為類似于zip,但是只有當(dāng)原始的Observable中的每一個(gè)都發(fā)射了一條數(shù)據(jù)時(shí)zip才發(fā)射數(shù)據(jù)咆霜。

    CombineLatest則在原始的Observable中任意一個(gè)發(fā)射了數(shù)據(jù)時(shí)發(fā)射一條數(shù)據(jù)邓馒。

    當(dāng)原始Observables的任何一個(gè)發(fā)射了一條數(shù)據(jù)時(shí),CombineLatest使用一個(gè)函數(shù)結(jié)合它們最近發(fā)射的數(shù)據(jù)蛾坯,然后發(fā)射這個(gè)函數(shù)的返回值光酣。

    本例中,含用戶名脉课、郵箱救军、密碼、注冊按鈕的注冊頁面的場景非常常見倘零,當(dāng)然可以使用普通的處理方式能夠達(dá)成唱遭,注冊按鈕的是否可用更改的效果,以及輸入是否合法的及時(shí)提示呈驶。

    但是使用RxJava的方式拷泽,代碼明顯簡潔、易懂袖瞻。

小結(jié):

雖然司致,上面四個(gè)使用場景主要介紹四個(gè)操作符的使用,但其實(shí)demo中穿插了不少其他操作符的使用虏辫,想要詳細(xì)了解的話蚌吸,代碼在這里锈拨。

暫時(shí)先寫到這里砌庄,后面會(huì)把其他自己學(xué)會(huì)的的操作符,寫成系列文章奕枢。如有興趣娄昆,請關(guān)注我的github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缝彬,一起剝皮案震驚了整個(gè)濱河市萌焰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谷浅,老刑警劉巖扒俯,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異一疯,居然都是意外死亡撼玄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門墩邀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掌猛,“玉大人,你說我怎么就攤上這事眉睹±蟛纾” “怎么了废膘?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長慕蔚。 經(jīng)常有香客問我丐黄,道長,這世上最難降的妖魔是什么孔飒? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任孵稽,我火速辦了婚禮,結(jié)果婚禮上十偶,老公的妹妹穿的比我還像新娘菩鲜。我一直安慰自己,他們只是感情好惦积,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布接校。 她就那樣靜靜地躺著,像睡著了一般狮崩。 火紅的嫁衣襯著肌膚如雪蛛勉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天睦柴,我揣著相機(jī)與錄音诽凌,去河邊找鬼。 笑死坦敌,一個(gè)胖子當(dāng)著我的面吹牛侣诵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狱窘,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼杜顺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蘸炸?” 一聲冷哼從身側(cè)響起躬络,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搭儒,沒想到半個(gè)月后穷当,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淹禾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年馁菜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稀拐。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铲咨,到底是詐尸還是另有隱情躲胳,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布粹湃,位于F島的核電站,受9級(jí)特大地震影響孤钦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜觉鼻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一畅姊、第九天 我趴在偏房一處隱蔽的房頂上張望粗合。 院中可真熱鬧供屉,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哗魂,卻和暖如春肛走,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背录别。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工朽色, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人组题。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓葫男,卻偏偏與公主長得像,于是被迫代替她去往敵國和親崔列。 傳聞我的和親對象是個(gè)殘疾皇子腾誉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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