当前位置:   article > 正文

Android使用Rxjava、Retrofit处理json解析异常,只看这一篇就够了_io.reactivex.rxjava2:rxjava 解析json失败

io.reactivex.rxjava2:rxjava 解析json失败

概述

日常开发的时候,避免不了与后台打交道,最常见的就是前端发送请求,后台返回数据,然后将拿到的数据进行展示。现在我们开始模仿一个基本的网络请求,这里使用wanandroid提供的开放api作为请求对象,地址:http://www.wanandroid.com/blog/show/2 ,然后我们选择获取文章列表的一个接口 http://wanandroid.com/article/listproject/0/json

基本请求示例

1、导入Rxjava、Retrofit依赖:

//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
//rxjava2
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、定义BaseResponse类

wanandroid返回的json数据都有一个基本的格式,即

{
	data:T,
	errorCode: 0,
	errorMsg: ""
}
  • 1
  • 2
  • 3
  • 4
  • 5

那么,我们需定义一个BaseResponse类,由于data的数据类型不确定,可能是bean,可能是list,这里使用泛型来表示,简单代码如下:

public class BaseResponse<T> implements Parcelable {

    private int errorCode;
    private String error;
    private T data;

 	// ……忽略各种setter、getter方法

    /**
     * 判断请求是否成功
     * @return bool
     */
    public boolean isSuccess(){
        return getErrorCode() == 0;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3、配置Retrofit

根据文档,创建ApiService.class,并定义接口

//接口
public interface ApiService {
    /**
     * 获取首页文章列表
     */
    @GET("/article/listproject/{pageIndex}/json")
    Observable<BaseResponse<ArticleBean>> getArticleList(@Path("pageIndex") int pageIndex);
}

//接口对象
public class ApiServiceImpl {

    private ApiServiceImpl() {
        throw new RuntimeException("you can't new me");
    }
    public static ApiService getInstance() {
        return createApiService.apiService;
    }

    /**
     * Retrofit生成接口对象.
     */
    private static class createApiService {
        /**
         * Retrofit会根据传入的接口类.生成实例对象.
         */
        private static final ApiService apiService = RetrofitClient.getInstance().getApi(ApiService.class);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

Observable<BaseResponse>的ArticleBean是接口data的具体数据类型,接着我们创建Retrofit

OkHttpClient.Builder builder = new OkHttpClient.Builder()
                //链接超时
                .connectTimeout(90, TimeUnit.SECONDS)
                //读取超时
                .readTimeout(90, TimeUnit.SECONDS)
                //失败自动重连
                .retryOnConnectionFailure(true);
//初始化Retrofit并添加配置
Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .baseUrl(ApiService.BASE_URL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
public <T> T getApi(Class<T> clz) {
     return mRetrofit.create(clz);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4、发起请求

ApiServiceImpl.getInstance()
                .getArticleList(pageIndex)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<BaseResponse<ArticleBean>>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }

                    @Override
                    public void onNext(BaseResponse<ArticleBean> response) {
                        if (response.isSuccess()){
                            ArticleBean articleBean = response.getData();
                            // do something
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onComplete() {
                    }
                });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

OK!一切看起来都是这么美好,不过往往事与愿违。

json解析异常

这里请求成功,后台返回的成功的数据类型为

{
	data:{},
	errorCode: 0,
	errorMsg: ""
}
  • 1
  • 2
  • 3
  • 4
  • 5

这是没问题的,但是假设请求失败,后台返回的数据类型为

{
	data:[],
	errorCode: 400,
	errorMsg: "参数错误……"
}
  • 1
  • 2
  • 3
  • 4
  • 5

请求失败的时候返回的data是一个数组,甚至一个字符串文本,当gson解析的时候,会抛一个JsonParseException,导致拿不到errorCode与errorMsg。

处理json解析异常

好在办法还是有的,对于失败时候的回调处理,有两种方法:

方法一:

让后台统一规范修改

{
	data:{},
	errorCode: 400,
	errorMsg: "参数错误……"
}
  • 1
  • 2
  • 3
  • 4
  • 5

改成无论是成功或者失败,data都返回一个对象或者数组。(当然这个是后台好说话,不打人的情况)
显然,即便后台改了,这个也是治标不治本的方法,万一新写的接口没有遵循该规范,或者是新来的同事不知道该规范,这些都是有可能的。那现在我们来看看第二种方法。

方法二:

不知道各位还记得构建Retrofit的那段代码中,有一句.addConverterFactory(GsonConverterFactory.create())
这个ConverterFactory是支持自定义的,也就是说,我们可以自定义自己的converter,不懂ConverterFactory原理的,请参考该文章 秒懂Retrofit2之GsonConverter

自定义Converter

public class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private final TypeAdapter<BaseResponse<T>> adapter;

    ResponseBodyConverter(Gson gson, TypeToken<T> typeToken) {
        ParameterizedTypeImpl parameterizedType = 
        new ParameterizedTypeImpl(null, BaseResponse.class, typeToken.getType());
        //noinspection unchecked
        adapter = (TypeAdapter<BaseResponse<T>>) gson.getAdapter(TypeToken.get(parameterizedType));
    }

    @Override
    public T convert(@NonNull ResponseBody value) throws IOException {
        String json = value.string();
        //第一次解析
        BaseResponse obj = GsonUtils.GsonToBean(json, BaseResponse.class);
        if (!obj.isSuccess()) {
            //如果是服务端返回的错误码,则抛出自定义异常
            throw new ApiException(obj.getErrorCode(), obj.getError());
        }
        //第二次解析
        BaseResponse<T> result = adapter.fromJson(json);
        value.close();
        return result.getData();
    }
    // 省略部分ParameterizedTypeImpl代码
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我们首先看convert方法,首先获得ResponseBody 的值,用gson解析,第一次是判断code,如果服务端返回的不是成功的状态码,则抛出自定义的ApiException,该异常可以在DisposableObserver类的onError方法接收到;如果服务端返回的是成功的状态码,则对数据进行解析,最终返回data。
另外,ParameterizedTypeImpl是一个静态内部类(后面源码会放出),将基本泛型类型指定为BaseResponse,也就是说,原本

Observable<BaseResponse<ArticleBean>> getArticleList(@Path("pageIndex") int pageIndex);
  • 1

现在可以写成

Observable<ArticleBean> getArticleList(@Path("pageIndex") int pageIndex);
  • 1

去掉BaseResponse这一层。然后我们现在可以将rxjava的请求订阅也统一封装一下,请求错误的处理。

自定义DisposableObserver

public abstract class BaseResourceObserver<T> extends DisposableObserver<T> {

    /*========================= HttpException 异常 code ==========================*/

    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    public void onError(Throwable throwable) {
        //打印日志到控制台
        throwable.printStackTrace();
        //如果你某个地方不想使用全局错误处理,
        //则重写 onError(Throwable) 并将 super.onError(e); 删掉
        //如果你不仅想使用全局错误处理,还想加入自己的逻辑,
        //则重写 onError(Throwable) 并在 super.onError(e); 后面加入自己的逻辑
        String msg = requestHandle(throwable);
        Log.i("tag",msg);
    }

    @Override
    public void onComplete() {
    }

	/**
     * 统一处理Throwable
     * @param e e
     * @return msg
     */
    private String requestHandle(Throwable e) {
        String msg;
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            switch (httpException.code()) {
                case UNAUTHORIZED:
                case FORBIDDEN:
                case NOT_FOUND:
                case REQUEST_TIMEOUT:
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                default:
                    msg = "服务器错误";
                    break;
            }
        } else if (e instanceof ApiException) {
            //后台异常,在这里你可以toast弹窗或者进行其他处理,具体需根据业务结合
            ApiException apiException = (ApiException) e;
            msg = apiException.getMessage();
        } else if (e instanceof JsonParseException || e instanceof JSONException 
        || e instanceof ParseException) {
            msg = "解析错误";
        } else if (e instanceof ConnectException || e instanceof SocketTimeoutException 
        || e instanceof UnknownHostException) {
            msg = "连接失败,请检查网络";
        }  else if (e instanceof NumberFormatException){
            msg = "数字格式化异常";
        } else {
            msg = "请求失败";
        }
        return msg;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

替换我们自定义的Converter

Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .baseUrl(ApiService.BASE_URL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                //.addConverterFactory(GsonConverterFactory.create())
                //这里是自定义的GsonConverterFactory
                .addConverterFactory(MyConverterFactory.create())
                .build();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在activity请求,

Disposable disposable = ApiServiceImpl.getInstance()
                .getArticleList(pageIndex)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                // 这里使用的是subscribeWith操作符
                .subscribeWith(new BaseResourceObserver<ArticleBean>() {
                    @Override
                    public void onNext(ArticleBean articleBean) {
                       // do something
                    }
                });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在subscribeWith里面初始化我们的BaseResourceObserver,只需重写onNext方法即可,简单便捷。
OK,至此处理json解析异常算是完成了。

总结

1、请求成功基本数据类型一般是不变的;
2、请求失败,只解析code错误码,不解析data,这样就不会出现解析异常了。
最后附上simple源码 RetrofitConverterSimple

参考
Rxjava、Retrofit返回json数据解析异常处理

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/300803
推荐阅读
相关标签
  

闽ICP备14008679号