Retrofit與Rxjava的探索實踐

本文主要參考此篇文章力作,原文鏈接
[給 Android 開發(fā)者的 RxJava 詳解]
(http://gank.io/post/560e15be2dca930e00da1083#toc_3)


寫在前面
最近在摸索著Rxjava,學(xué)了一大半,但是深知要實踐與理論結(jié)合才能學(xué)得快也記得牢,然而最好的實踐是什么呢?可能是我學(xué)得還不夠深,覺得它的好處在網(wǎng)絡(luò)請求這邊特別明顯,于是網(wǎng)絡(luò)請求網(wǎng)絡(luò)請求網(wǎng)絡(luò)請求。。。
發(fā)現(xiàn)有做得更好的東西,那就是Retrofit與Rxjava這兩個小情侶特別好,所以就再次看了一下這兩個的實踐,發(fā)現(xiàn),真的有了一個新大陸~然后就沒什么好說的了,搞起唄。
本次實踐基于androidstudio,所以很多庫的依賴都使用gradle來配置


實踐1 庫的安裝

首先依賴rxjava

  compile 'io.reactivex:rxjava:x.y.z'
  compile 'io.reactivex:rxandroid:1.0.1'

接下來依賴retrofit

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'//使用Gson解析
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'//異常時的處理

實踐2 處理場景

我們假設(shè)使用的場景是輸入賬號密碼,請求網(wǎng)絡(luò)進(jìn)行賬號驗證,驗證成功就直接登錄,不過在登錄之前需要獲取到Token,根據(jù)Token和輸入的賬號密碼進(jìn)行登錄驗證。
所以我們需要兩個方法,一個獲取token,一個登錄返回結(jié)果。
假設(shè)我們返回的數(shù)據(jù)結(jié)構(gòu)是固定的,就像以下:

{
  "code":0,
  "message":"success",
  "data":{
  ...
  }
}

實踐3 代碼實現(xiàn)

  • 首先有基礎(chǔ)的Retrofit和rxjava的請求,這邊叫RxReService
public interface RxReService {
@POST("user/login")
    Observable<String> login(
            @Query("token") String token,
            @Query("username") String name,
            @Query("password") String psd);
}
@POST("token")
    Observable<String> getToken();

這是一個接口,通用標(biāo)注的方式傳入url,使用query方式添加參數(shù)

  • 接口寫好了,看一下實際的調(diào)用,叫RxReRequest
public class RxReRequest{
    private static String BASEURL = "https://www.xxxx.com/";
    private static RxReService mRxReService;
}
     public static void initRxRe() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASEURL)
                .build();
        mRxReService = retrofit.create(RxReService.class);
    }
     public static Observable<String> getToken(){
        return  mRxReService.getToken();
    }
     public static Observable<String> login(String name,String psd,String token){
        return mRxReService.login(token,name,psd);
    }

其實就兩個方法,getToken和login,這邊是對其請求進(jìn)行簡單封裝

  • 簡單使用
RxReRequestHelper.login("", "", new ProgressSubscribe<String>(new SubscriberOnNextListener<String>() {
            @Override
            public void onNext(String result) {

            }
        }, MainActivity.this));
        RxReRequest.getToken().subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String userTokenResultData) {
                //對String進(jìn)行解析
                ...
                //解析完得到toekn
                String token = token.getToken();
                RxReRequest.login("name","psd",token).subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(String comResultResultData) {
 //再次解析
                        ...
                    }
                });
            }
        });

一句我的天啊,日了狗,這代碼。。。
莫急莫急,待我慢慢道來。
我們這邊是用比較復(fù)雜的請求才能看出rxjava和retrofit的便利之處

實踐4 封裝

我們首先來說一下
首先是使用gson解析的話,retrofit已經(jīng)做了很好的處理,只需在initRxRe這個地方添加一個參數(shù)

 .addConverterFactory(GsonConverterFactory.create())//添加Gson解析庫

順便說一下這個

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加取消訂閱時取消http請求

這個是當(dāng)取消訂閱時自動取消http請求
有了以上這些,我們就可以進(jìn)行后面的工作了

4-1 請求結(jié)束自動解析

添加以上兩個參數(shù)后,我們的RxReService就變成

Observable<ComResult> login(
            @Query("token") String token,
            @Query("username") String name,
            @Query("password") String psd);
 @POST("token")
    Observable<UserToken> getToken();

返回的類型就直接轉(zhuǎn)換成我們要的最終類型

RxReRequest的兩個方法變成

 public static Observable<UserToken> getToken(){
        return  mRxReService.getToken();
    }
    public static Observable<ComResult> login(String name, String psd, String token){
        return mRxReService.login(token,name,psd);
    }

因此最后的使用變成

 RxReRequest.getToken().subscribe(new Subscriber<UserToken>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(UserToken userTokenResultData) {
                RxReRequest.login("name","psd",userTokenResultData.getToken()).subscribe(new Subscriber<ComResult>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(ComResult comResultResultData) {
                    }
                });
            }
        });

Gson解析省略掉了,不過看了還是有些別扭我們發(fā)現(xiàn)其實需要的就onErr和onNext兩個方法而已,還有我們解析的會將整個返回值都每次解析出來,但是我們的返回格式是固定的呀,我每次只要根據(jù)code拿數(shù)據(jù)實例就好了,那就是提前預(yù)解析

4-2 提前預(yù)解析

再啰嗦一下,假設(shè)我們的返回結(jié)果是

{
  "code":0,
  "message":"success",
  "data":{
  ...
  }
}

那我們就可以每次先對結(jié)果進(jìn)行解析,如果是code不等于0那就不解析了,所以我們需要這么一個類

public class ResultData<T> {
    private int resultCode;
    private String message;
    private T data;

    public int getResultCode() {
        return resultCode;
    }

    public String getMessage() {
        return message;
    }

    public T getData() {
        return data;
    }
}

因為這個實例數(shù)據(jù)是不固定的,所以只能用泛型來做,所以我們的RxReService又變了

@POST("user/login")
   Observable<ResultData<ComResult>> login(
           @Query("token") String token,
           @Query("username") String name,
           @Query("password") String psd);

跟著變的RxReRequest

public static Observable<ResultData<UserToken>> getToken() {
        return mRxReService.getToken();
    }

    public static Observable<ResultData<ComResult>> login(String name, String psd, String token) {
        return mRxReService.login(token, name, psd);
    }

我們在哪里進(jìn)行預(yù)解析呢?當(dāng)然是用rxjava牛逼閃閃的map關(guān)鍵字了。我們預(yù)解析的目的是當(dāng)code不為0調(diào)用onErr方法,而不僅僅是訪問出錯,這樣我們在onNext那邊只需關(guān)心正確的數(shù)據(jù)就是了
我們知道m(xù)ap的參數(shù)

 public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
        return lift(new OperatorMap<T, R>(func));
    }

所以我們需要定義一個func1來繼承這個Func1

public class HttpResultFunc<T> implements Func1<ResultData<T>, T> {
    @Override
    public T call(ResultData<T> tResultData) {
        if (tResultData.getResultCode() != 0) {
            throw new ResultException(tResultData.getResultCode(), tResultData.getMessage());
        }
        return tResultData.getData();
    }
}

我們怎么做的呢?就是當(dāng)code不等于0就拋出一個異常,讓onErr接收到這個異常,但是這個異常又要包含到數(shù)據(jù)異常的信息,所以我們還需要自己定義一個異常

public class ResultException extends RuntimeException {

    private int errorCode;
    private String errMessage;

    public ResultException(int errorCode, String errMessage) {
        this.errorCode = errorCode;
        this.errMessage = errMessage;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public String getErrMessage() {
        return errMessage;
    }
}

里面包含了errCode和errMessage
好這邊我們改變一下RxReRequest

public static void getToken(Subscriber<UserToken> subscriber) {
       mRxReService.getToken().map(new HttpResultFunc<UserToken>()).subscribe(subscriber);
   }

   public static void login(String name, String psd, String token, Subscriber<ComResult> subscriber) {
       mRxReService.login(token, name, psd).map(new HttpResultFunc<ComResult>()).subscribe(subscriber);
   }

我們使用了map對其進(jìn)行預(yù)解析
好的,我們看看怎么使用

 RxReRequest.getToken(new Subscriber<UserToken>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(UserToken userToken) {
                RxReRequest.login("", "", userToken.getToken(), new Subscriber<ComResult>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(ComResult comResult) {

                    }
                });
            }
        });

這邊onNext里面都是實在的數(shù)據(jù),不會再有數(shù)據(jù)為空時會跑進(jìn)去了
還有一個小東西,我們發(fā)現(xiàn)onComplete是沒用的,那我們也給他去掉吧~~怎么做,自己定義咯

4-3 去掉onComplete

我們自定義一個觀察者,也是抽象類

public abstract class ResutSubscriber<T> extends Subscriber<T>{
    @Override
    public void onCompleted() {
        //結(jié)束
    }
}

當(dāng)然繼承免不了,這邊可以做個打印啊還是啥的,當(dāng)然如果這個有用到,就不能這樣做啦
好的,跟著改變的是這邊RxReRequest

    public static void getToken(ResutSubscriber<UserToken> subscriber) {
        mRxReService.getToken().map(new HttpResultFunc<UserToken>()).subscribe(subscriber);
    }

    public static void login(String name, String psd, String token, ResutSubscriber<ComResult> subscriber) {
        mRxReService.login(token, name, psd).map(new HttpResultFunc<ComResult>()).subscribe(subscriber);
    }

這樣使用的話就簡便了一點點

RxReRequest.getToken(new ResutSubscriber<UserToken>() {

            @Override
            public void onError(Throwable e) {
                if (e instanceof ResultException){
                    Log.e("err",((ResultException)e).getErrMessage() +((ResultException)e).getErrorCode() );
                }else {
                    Log.e("err","請求異常");
                }
            }

            @Override
            public void onNext(UserToken userToken) {
                RxReRequest.login("", "", userToken.getToken(), new ResutSubscriber<ComResult>() {

                    @Override
                    public void onError(Throwable e) {
                        if (e instanceof ResultException){
                            Log.e("err",((ResultException)e).getErrMessage() +((ResultException)e).getErrorCode() );
                        }else {
                            Log.e("err","請求異常");
                        }
                    }

                    @Override
                    public void onNext(ComResult comResult) {

                    }
                });
            }
        });

這樣沒用的代碼就沒掉了,不過這個嵌套的網(wǎng)絡(luò)請求看了總是不開心,怎么辦呢。。。我們知道rxjava還有flatMap,那就用上吧。

4-4使用flatmap處理需要兩級請求的情況

flatmap的解釋很不好說很不好說也不知道怎么說,在這個場景具體的我們可以理解為,請求登錄的話會先要求獲取token,獲取到了在執(zhí)行登錄的請求,那就上代碼吧。
我們把兩個方法合成一個方法
于是RxReRequest的登錄方法里面要做兩件事,一件是獲取token,一件是登錄,所以

  public static void login(final String name, final String psd, final ResutSubscriber<ComResult> subscriber) {
        mRxReService.getToken().map(new HttpResultFunc<UserToken>()).flatMap(new Func1<UserToken, Observable<ComResult>>() {
            @Override
            public Observable<ComResult> call(UserToken userToken) {
                return mRxReService.login(userToken.getToken(), name, psd).map(new HttpResultFunc<ComResult>());
            }
        }).subscribe(subscriber);
    }

最后的使用

    RxReRequest.login("", "", new ResutSubscriber<ComResult>() {
            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(ComResult comResult) {

            }
        });

這下,整個世界都清凈了~~~~

4-5 線程切換

我們知道安卓是不能在主線程進(jìn)行耗時操作的,包括網(wǎng)絡(luò)請求,所以我們?nèi)绻瓷厦娴膩碜龅脑挿址昼姃伄惓?,所以還需要一把殺手锏,就是線程切換。
使用Rxjava可以很方便進(jìn)行線程切換,當(dāng)進(jìn)行網(wǎng)絡(luò)請求時,線程切換到子線程(另外新建一個線程),請求結(jié)束后切換到主線程
RxReRequest里面的請求

 public static void getToken(ResutSubscriber<UserToken> subscriber) {
        mRxReService.getToken().map(new HttpResultFunc<UserToken>()).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
    }
 public static void login(final String name, final String psd, final ResutSubscriber<ComResult> subscriber) {
        mRxReService.getToken().map(new HttpResultFunc<UserToken>()).flatMap(new Func1<UserToken, Observable<ComResult>>() {
            @Override
            public Observable<ComResult> call(UserToken userToken) {
                return mRxReService.login(userToken.getToken(), name, psd).map(new HttpResultFunc<ComResult>());
            }
        }).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
    }

subscribeOn表示事件發(fā)生時所在的線程,這邊指定在新建的線程,observeOn則指定觀察者發(fā)生的事件的線程,我們指定在主線程。

當(dāng)需要loading的時候

因為我們網(wǎng)絡(luò)請求的觀察者都發(fā)生在主線程,所以我們還是自己定義一個觀察者,里面包含開始和結(jié)束,在開始的地方顯示dialog,在結(jié)束或者出錯的地方做響應(yīng)的取消dialog或者顯示錯誤信息,還可以根據(jù)出錯的errcode進(jìn)行靈活展示

public abstract class ProgressSubscribe<T> extends Subscriber<T> {

    private Context mContext;
    private Handler mHandler;

    public ProgressSubscribe(Context mContext) {
        this.mContext = mContext;
        mHandler = new Handler();
    }

    @Override
    public void onCompleted() {
        //dismissDialog
    }
    @Override
    public void onStart() {
        //showDialog

    }\ @Override
    public void onError(Throwable e) {
        //錯誤時的處理
    }
}

這樣錯誤的進(jìn)行統(tǒng)一處理,我們就只要關(guān)注有數(shù)據(jù)的業(yè)務(wù)邏輯就行了。。。。



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容