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

轉載請註明出處:http://www.wangxinarhat.com/2016/04/19/2016-04-19-rxjava-android-operate1/java

最近學習了RxJava在android中的使用,關於RxJava是啥,爲何要用RxJava,好在哪,這裏就不敘述了,若是想要了解請移步官方文檔大神文章

這裏只講解一下RxJava中的操做符在項目中具體的使用場景。

由於學習了有20個操做符,可能一篇文章過於臃腫,因此打算寫成系列文章,本文中全部操做符的使用,都寫在了一個demo中,已上傳至githubreact

場景一:RxJava基本使用

配合Retrofit請求網絡數據,若是你對Retrofit不熟悉就先看Retrofit官網,實現步驟以下:android

  1. 先是build.gradle的配置git

    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來進行的。添加rxandroid是由於rxjava中的線程問題。github

  2. 基本網絡請求使用準備
    咱們使用http://zhuangbi.info/search?q=param測試鏈接,返回的是json格式,代碼就不貼了。接下來咱們要建立一個接口取名爲ZhuangbiApi,代碼以下:json

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

    Retrofit、Gson、RxJava結合使用,創建網絡請求類:api

    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. 具體使用
    將要查詢的關鍵字傳進去,使用上面創建的網絡請求類請求數據,並在訂閱者的回調方法中,進行網絡請求結果的處理網絡

    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;
        }
  4. 詳解
    search方法中傳入的key是要查詢的關鍵詞,getObserver()是獲取訂閱者對象,並在其回調方法中根據返回結果,作相應處理:app

    • 其中onNext方法返回了數據,這樣咱們可以在onNext裏面處理數據相關的邏輯;異步

    • onError方法中處理錯誤,同時也能夠中止ProgressDialog等;

    • onComplated只調用一次結束本次請求操做,也能夠中止ProgressDialog;

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

對Observable發射的每一項數據應用一個函數,執行變換爲指定類型的操做,而後再發射。

有些服務端的接口設計,會在返回的數據外層包裹一些額外信息,這些信息對於調試頗有用,但本地顯示是用不到的。使用 map() 能夠把外層的格式剝掉,只留下咱們只關心的部分,具體實現步驟以下:

  1. 網絡請求使用準備
    咱們使用http://gank.io/api/測試鏈接
    接下來咱們要建立一個接口取名爲GankApi ,代碼以下:

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

    Retrofit、Gson、RxJava結合使用,創建網絡請求類:

    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. 數據轉換
    返回數據就不貼了,有興趣能夠請求接口看一下。
    接口返回的數據包含了一些額外的信息,可是咱們只須要返回數據中的list部分,因此建立一個類,來實現數據轉換的功能,代碼以下:

    public class BeautyResult2Beautise implements Func1<BeautyResult, List<ImageInfoBean>> {
    
    
        public static BeautyResult2Beautise newInstance() {
            return new BeautyResult2Beautise();
        }
    
    
        /**
         * 將接口返回的BeautyResult數據中的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;
        }
    }
  3. 操做符的使用
    加載數據

    /**
         * 加載數據的方法
         * @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;
        }
  4. 詳解
    Map操做符對Observable發射的每一項數據應用一個函數,執行變換操做,而後返回一個發射這些結果的Observable。
    本例中,接口返回的數據格式是:

    public class BeautyResult {
    
    
        public boolean error;
    
        public List<ImageInfoBean> results;
    
    
    }

    可是咱們只關心list部分的數據,因此進行轉換操做,這樣訂閱者回調方法中拿到的數據直接進行使用就行了

場景三:Zip操做符的使用(結合)

經過一個函數將多個Observables的發射物結合到一塊兒,基於這個函數的結果爲每一個結合體發射單個數據項,具體實現步驟以下:

  1. 網絡請求裝備
    網絡請求Api,以及請求類,仍是使用場景1、二中的建立好的。

  2. 請求數據,並結合,代碼以下:

    /**
         * 請求兩個接口,對返回的數據進行結合
         */
        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());
        }
  3. 詳解
    請求GankApi中的數據使用map操做符進行轉換,取出本身想要的list數據,而後結合ZhuangbiApi中的數據,造成新的數據集合,填充到view。

    Zip操做符使用函數按順序結合多個Observables發射的數據項,而後它發射這個函數返回的結果,它只發射與數據項最少的那個Observable同樣多的數據。

    通常app中同一個界面有時會須要同時訪問不一樣接口,而後將結果糅合後轉爲統一的格式後輸出(例如將第三方廣告 API 的廣告夾雜進自家平臺返回的數據 List 中)。這種並行的異步處理比較麻煩,不過用了 zip() 以後就會簡單明瞭。
    上一個效果圖:
    圖片描述
    能夠看出,recyclerView中使用了一個數據集合,但左側的一列展現的是GankApi中的數據,右側一列展現的是ZhuangbiApi 中的數據。

場景四:CombineLatest操做符的使用(結合)

結合多個Observable發射的最近數據項,當原始Observables的任何一個發射了一條數據時,CombineLatest使用一個函數結合它們最近發射的數據,而後發射這個函數的返回值,具體實現步驟以下:

  1. 使用場景,用一個簡單明瞭的圖片來表示吧
    圖片描述

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

    /**
         * 將3個EditText的事件進行結合
         */
        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) {
                    //更改註冊按鈕是否可用的狀態
                    mButton.setEnabled(aBoolean);
                }
            };
        }
  3. 詳解
    CombineLatest操做符行爲相似於zip,可是隻有當原始的Observable中的每個都發射了一條數據時zip才發射數據。

    CombineLatest則在原始的Observable中任意一個發射了數據時發射一條數據。

    當原始Observables的任何一個發射了一條數據時,CombineLatest使用一個函數結合它們最近發射的數據,而後發射這個函數的返回值。

    本例中,含用戶名、郵箱、密碼、註冊按鈕的註冊頁面的場景很是常見,固然可使用普通的處理方式可以達成,註冊按鈕的是否可用更改的效果,以及輸入是否合法的及時提示。

    可是使用RxJava的方式,代碼明顯簡潔、易懂。

小結

雖然,上面四個使用場景主要介紹四個操做符的使用,但其實demo中穿插了很多其餘操做符的使用,想要詳細瞭解的話,代碼在這裏

暫時先寫到這裏,後面會把其餘本身學會的的操做符,寫成系列文章。若有興趣,請關注個人github

相關文章
相關標籤/搜索