轉載請註明出處:http://www.wangxinarhat.com/2016/04/19/2016-04-19-rxjava-android-operate1/java
最近學習了RxJava在android中的使用,關於RxJava是啥,爲何要用RxJava,好在哪,這裏就不敘述了,若是想要了解請移步官方文檔、大神文章。
這裏只講解一下RxJava中的操做符在項目中具體的使用場景。
由於學習了有20個操做符,可能一篇文章過於臃腫,因此打算寫成系列文章,本文中全部操做符的使用,都寫在了一個demo中,已上傳至githubreact
配合Retrofit請求網絡數據,若是你對Retrofit不熟悉就先看Retrofit官網,實現步驟以下:android
先是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
基本網絡請求使用準備
咱們使用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; }
具體使用
將要查詢的關鍵字傳進去,使用上面創建的網絡請求類請求數據,並在訂閱者的回調方法中,進行網絡請求結果的處理網絡
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; }
詳解
search方法中傳入的key是要查詢的關鍵詞,getObserver()是獲取訂閱者對象,並在其回調方法中根據返回結果,作相應處理:app
其中onNext方法返回了數據,這樣咱們可以在onNext裏面處理數據相關的邏輯;異步
onError方法中處理錯誤,同時也能夠中止ProgressDialog等;
onComplated只調用一次結束本次請求操做,也能夠中止ProgressDialog;
對Observable發射的每一項數據應用一個函數,執行變換爲指定類型的操做,而後再發射。
有些服務端的接口設計,會在返回的數據外層包裹一些額外信息,這些信息對於調試頗有用,但本地顯示是用不到的。使用 map() 能夠把外層的格式剝掉,只留下咱們只關心的部分,具體實現步驟以下:
網絡請求使用準備
咱們使用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; }
數據轉換
返回數據就不貼了,有興趣能夠請求接口看一下。
接口返回的數據包含了一些額外的信息,可是咱們只須要返回數據中的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; } }
操做符的使用
加載數據
/** * 加載數據的方法 * @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; }
詳解
Map操做符對Observable發射的每一項數據應用一個函數,執行變換操做,而後返回一個發射這些結果的Observable。
本例中,接口返回的數據格式是:
public class BeautyResult { public boolean error; public List<ImageInfoBean> results; }
可是咱們只關心list部分的數據,因此進行轉換操做,這樣訂閱者回調方法中拿到的數據直接進行使用就行了
經過一個函數將多個Observables的發射物結合到一塊兒,基於這個函數的結果爲每一個結合體發射單個數據項,具體實現步驟以下:
網絡請求裝備
網絡請求Api,以及請求類,仍是使用場景1、二中的建立好的。
請求數據,並結合,代碼以下:
/** * 請求兩個接口,對返回的數據進行結合 */ 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()); }
詳解
請求GankApi中的數據使用map操做符進行轉換,取出本身想要的list數據,而後結合ZhuangbiApi中的數據,造成新的數據集合,填充到view。
Zip操做符使用函數按順序結合多個Observables發射的數據項,而後它發射這個函數返回的結果,它只發射與數據項最少的那個Observable同樣多的數據。
通常app中同一個界面有時會須要同時訪問不一樣接口,而後將結果糅合後轉爲統一的格式後輸出(例如將第三方廣告 API 的廣告夾雜進自家平臺返回的數據 List 中)。這種並行的異步處理比較麻煩,不過用了 zip() 以後就會簡單明瞭。
上一個效果圖:
能夠看出,recyclerView中使用了一個數據集合,但左側的一列展現的是GankApi中的數據,右側一列展現的是ZhuangbiApi 中的數據。
結合多個Observable發射的最近數據項,當原始Observables的任何一個發射了一條數據時,CombineLatest使用一個函數結合它們最近發射的數據,而後發射這個函數的返回值,具體實現步驟以下:
使用場景,用一個簡單明瞭的圖片來表示吧
上圖簡單演示了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); } }; }
詳解
CombineLatest操做符行爲相似於zip,可是隻有當原始的Observable中的每個都發射了一條數據時zip才發射數據。
CombineLatest則在原始的Observable中任意一個發射了數據時發射一條數據。
當原始Observables的任何一個發射了一條數據時,CombineLatest使用一個函數結合它們最近發射的數據,而後發射這個函數的返回值。
本例中,含用戶名、郵箱、密碼、註冊按鈕的註冊頁面的場景很是常見,固然可使用普通的處理方式可以達成,註冊按鈕的是否可用更改的效果,以及輸入是否合法的及時提示。
可是使用RxJava的方式,代碼明顯簡潔、易懂。
雖然,上面四個使用場景主要介紹四個操做符的使用,但其實demo中穿插了很多其餘操做符的使用,想要詳細瞭解的話,代碼在這裏。
暫時先寫到這裏,後面會把其餘本身學會的的操做符,寫成系列文章。若有興趣,請關注個人github。