Rxjava、Retrofit返回json數據解析異常處理

每一個App都避免不了要進行網絡請求,從最開始的用谷歌封裝的volley到再到android-async-http再到OKHttpUtils再到如今的Retrofit和RxJava,從我本身用後的體驗來看,用了retrofit和RxJava真的回不去了,回不去了,不去了,去了,了…(哈哈,原本還想分析下這四個的區別,網上這樣的文章不少,我就不必多添亂了-.-)。很少逼逼,下面開始正文。java

一、Rxjava和Retrofit依賴導入:

compile 'io.reactivex:rxandroid:1.2.0'                       //Rxjava專門針對anroid封裝的RxAndroid
compile 'io.reactivex:rxjava:1.1.5'                          
compile 'com.squareup.retrofit2:retrofit:2.0.2'              //retrofit
compile 'com.squareup.retrofit2:converter-gson:2.0.2'        //gson converter
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'        //Retrofit專門爲Rxjava封裝的適配器
compile 'com.google.code.gson:gson:2.6.2'                    //Gson
compile 'com.squareup.okhttp3:logging-interceptor:3.1.2'     //打印網絡請求的log日誌

二、網絡請求基類的配置

創建一個工廠類react

public class ServiceFactory {

    private final Gson mGsonDateFormat;


    public ServiceFactory(){
        mGsonDateFormat = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd hh:mm:ss")
                .create();
    }


    private static class SingletonHolder{
        private static final ServiceFactory INSTANCE = new ServiceFactory();
    }


    public static ServiceFactory getInstance(){
        return SingletonHolder.INSTANCE;
    }


    public <S> createService(Class<S> serviceClass){
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constant.BASE_URL) //Retrofit2 base url  必須是這種格式的:http://xxx.xxx/
                .client(getOkHttpClient())

    --------------------------添加Gson工廠變換器也就是不用管數據解析-----------------------------------
                .addConverterFactory(GsonConverterFactory.create())     
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        return retrofit.create(serviceClass);

    }

    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        @Override
        public void log(String message) {
            //打印retrofit日誌
            Log.i("RetrofitLog","retrofitBack ======================= "+message);
        }
    });


    private static final long DEFAULT_TIMEOUT = 10;
    private OkHttpClient getOkHttpClient(){
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //定製OkHttp
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
        builder.addInterceptor(loggingInterceptor);
        //設置緩存
        File httpCacheDirectory = new File(SDCardUtils.getRootDirectoryPath(),"這裏是你的網絡緩存存放的地址");
        builder.cache(new Cache(httpCacheDirectory,10*1024*1024));

        return builder.build();
    }

}

好了 下一步 咱們要建一個網絡請求的基類,通常網絡請求返回的數據最外層的根式就是 code msg result,可變的就是result,因此咱們把result的類型定義爲一個泛型的android

public class HttpResult<T> extends BaseEntity {
    public int code;
    private boolean isSuccess;
    private T result;
    private String msg;

    public void setMsg(String msg) {
        this.msg = msg;
    }


    public T getResult() {
        return result;
    }


    public void setResult(T result) {
        this.result = result;

    }

    public boolean isSuccess() {
        return code == 200;
    }

    public int getCode() {
        return code;
    }
}

既然咱們的網絡請求是rxjava配合retrofit 下面就定義咱們的網絡請求訂閱subscriberjson

public abstract class HttpResultSubscriber<T> extends Subscriber<HttpResult<T>> {
    @Override
    public void onNext(HttpResult<T> t) {
        if (t.isSuccess()) {
            onSuccess(t.getResult());
        } else {
            _onError(t.getMsg().getCode());
        }
    }

    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {
        e.printStackTrace();
        //在這裏作全局的錯誤處理
        if (e instanceof ConnectException ||
                e instanceof SocketTimeoutException ||
                e instanceof TimeoutException) {
            //網絡錯誤
            _onError(-9999);
        }
    }


    public abstract void onSuccess(T t);

    public abstract void _onError(int status);
}

OK咱們的網絡請求基類已經完成啦!下面開始咱們的網絡請求數組

首先咱們定義一個接口(以登陸爲例):緩存

public interface LoginService {

    //這個例子是post爲例,若是想要了解其餘的網絡請求,請點擊文章開始出的retrofit連接
    @FormUrlEncoded
    @POST(Constant.LOGIN_URL)   這裏是你的登陸url

    //能夠看到咱們的登陸返回的是一個Observable,它裏面包含的使咱們的網絡請求返回的實體基類,

    //而咱們實體基類的result如今就是UserInfoEntity
    Observable<HttpResult<UserInfoEntity>> login(@Field("mobile") String phone,
                                 @Field("password") String pwd);
}

如今開始咱們的網絡請求啦網絡

public void login(String phone, String pwd) {
    ServiceFactory.getInstance()
            .createService(LoginService.class)
            .login(phone,pwd)
            .compose(TransformUtils.<HttpResult<UserInfoEntity>>defaultSchedulers())
            .subscribe(new HttpResultSubscriber<UserInfoEntity>() {
                @Override
                public void onSuccess(UserInfoEntity userInfoEntity) {
                     //這是網絡請求陳宮的回調
                }

                @Override
                public void _onError(int status) {
                    //這是失敗的回調 根據status作具體的操做
                }
            });
}

你覺得這樣就好了 ,  這樣子確實沒毛病,確實已經妥妥的了。但是,但是,事與願違啊!!!async

三、具體解決辦法

通常狀況這是咱們的返回json格式:ide

{
    "code":200,
        "msg":"成功",
        "result":{}
}
咱們剛纔定義登陸接口的時候 返回的實體基類例傳入的是UserInfoEntity  這確實是沒問題的 但是大家加入登陸失敗的時候返回的json數據格式是這樣的怎麼辦?

{
    "code":300,
    "msg":"成功",
    "result":[]
}

失敗的時候返回的實體又是一個數組,這樣子就會抱一個json解析異常拿很少失敗的狀態碼和提示信息post

OK其實咱們的網絡請求已經完成90%了,剩下的就是不重要的失敗的時候回調了。

方法一:(這是在後臺兄弟好說話,並且不打人的狀況下…固然這種好人,仍是有的,不過這不是咱們今天要講的重點)

咱們可讓後臺返回的json數據中的result永遠是個數組。

{
    "code":300,
    "msg":"成功",
    "result":[]
}

方法二:

首先給你們看一個圖片

 

這就是咱們要下手的地方
上面咱們添加的工廠變換器是導入的依賴 compile 'com.squareup.retrofit2:converter-gson:2.0.2' 這個提供的

那可能有人要問了,那咱們不用這個用哪一個啊,不着急,不着急。還好retrofit是支持自定義的ConverterFactory的

下面咱們就開始咱們的自定義征程吧。

一、自定義Gson響應體變換器

public class GsonResponseBodyConverter<T> implements Converter<ResponseBody,T>{
    private final Gson gson;
    private final Type type;


    public GsonResponseBodyConverter(Gson gson,Type type){
        this.gson = gson;
        this.type = type;
    }
    @Override
    public T convert(ResponseBody value) throws IOException {

        String response = value.string();
        //先將返回的json數據解析到Response中,若是code==200,則解析到咱們的實體基類中,不然拋異常
        Response httpResult = gson.fromJson(response, Response.class);
        if (httpResult.getCode()==200){
            //200的時候就直接解析,不可能出現解析異常。由於咱們實體基類中傳入的泛型,就是數據成功時候的格式
            return gson.fromJson(response,type);
        }else {
            ErrorResponse errorResponse = gson.fromJson(response,ErrorResponse.class);
            //拋一個自定義ResultException 傳入失敗時候的狀態碼,和信息
            throw new ResultException(errorResponse.getCode(),errorResponse.getMsg());
        }
    }
}

二、自定義一個響應變換工廠 繼承自 retrofit的 converter.Factory

public class ResponseConverterFactory extends Converter.Factory {

    public static ResponseConverterFactory create() {
        return create(new Gson());
    }


    public static ResponseConverterFactory create(Gson gson) {
        return new ResponseConverterFactory(gson);
    }

    private final Gson gson;

    private ResponseConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        //返回咱們自定義的Gson響應體變換器
        return new GsonResponseBodyConverter<>(gson, type);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        //返回咱們自定義的Gson響應體變換器
        return new GsonResponseBodyConverter<>(gson,type);
    }
}

而後將上面的GsonConverterFactory.create() 替換成咱們自定義的ResponseConverterFactory.create()。

public <S> S createService(Class<S> serviceClass){
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constant.BASE_URL)
            .client(getOkHttpClient())
            //.addConverterFactory(GsonConverterFactory.create())
            //而後將上面的GsonConverterFactory.create()替換成咱們自定義的ResponseConverterFactory.create()
            .addConverterFactory(ResponseConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
    return retrofit.create(serviceClass);

}

再而後,最後一個而後啦(-.-)

在咱們的自定義的Rxjava訂閱者 subscriber中的onError()中加入咱們剛纔定義的ResultException。

@Override
public void onError(Throwable e) {
    e.printStackTrace();
    //在這裏作全局的錯誤處理
    if (e instanceof ConnectException ||
            e instanceof SocketTimeoutException ||
            e instanceof TimeoutException) {
        //網絡錯誤
        _onError(-9999);
    } else if (e instanceof ResultException) {
        //自定義的ResultException
        //因爲返回200,300返回格式不統一的問題,自定義GsonResponseBodyConverter凡是300的直接拋異常
        _onError(((ResultException) e).getErrCode());
        System.out.println("---------errorCode------->"+((ResultException) e).getErrCode());
    }
}

此次是真的完成了咱們的json數據解析異常的處理,其實咱們的解決辦法是解析了兩次,第一次解析的時候咱們的Response中只有只是解析了最外層的 code 和 msg  ,result中的是沒有解析的。response中的code==200,直接將數據解析到咱們的實體基類中。若是code!=200時,直接拋自定義的異常,直接會回調到subscriber中的onError()中。雖然進行了兩次解析,可是第一次只是解析了code,和msg 對於效率的影響其實並不大,在功能實現的基礎上一點點效率的影響(並且這個影響是微乎其微的-.-)其實無傷大雅的。

相關文章
相關標籤/搜索