java simple json解析,Android使用Rxjava、Retrofit处理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'

2、定义BaseResponse类

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

{

data:T,

errorCode: 0,

errorMsg: ""

}

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

public class BaseResponse implements Parcelable {

private int errorCode;

private String error;

private T data;

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

/**

* 判断请求是否成功

* @return bool

*/

public boolean isSuccess(){

return getErrorCode() == 0;

}

}

3、配置Retrofit

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

//接口

public interface ApiService {

/**

* 获取首页文章列表

*/

@GET("/article/listproject/{pageIndex}/json")

Observable> 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);

}

}

Observable的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 getApi(Class clz) {

return mRetrofit.create(clz);

}

4、发起请求

ApiServiceImpl.getInstance()

.getArticleList(pageIndex)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Observer>() {

@Override

public void onSubscribe(Disposable d) {

}

@Override

public void onNext(BaseResponse response) {

if (response.isSuccess()){

ArticleBean articleBean = response.getData();

// do something

}

}

@Override

public void onError(Throwable e) {

}

@Override

public void onComplete() {

}

});

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

json解析异常

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

{

data:{},

errorCode: 0,

errorMsg: ""

}

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

{

data:[],

errorCode: 400,

errorMsg: "参数错误……"

}

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

处理json解析异常

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

方法一:

让后台统一规范修改

{

data:{},

errorCode: 400,

errorMsg: "参数错误……"

}

改成无论是成功或者失败,data都返回一个对象或者数组。(当然这个是后台好说话,不打人的情况)

显然,即便后台改了,这个也是治标不治本的方法,万一新写的接口没有遵循该规范,或者是新来的同事不知道该规范,这些都是有可能的。那现在我们来看看第二种方法。

方法二:

不知道各位还记得构建Retrofit的那段代码中,有一句.addConverterFactory(GsonConverterFactory.create())

这个ConverterFactory是支持自定义的,也就是说,我们可以自定义自己的converter,不懂ConverterFactory原理的,请参考该文章 秒懂Retrofit2之GsonConverter

自定义Converter

public class ResponseBodyConverter implements Converter {

private final TypeAdapter> adapter;

ResponseBodyConverter(Gson gson, TypeToken typeToken) {

ParameterizedTypeImpl parameterizedType =

new ParameterizedTypeImpl(null, BaseResponse.class, typeToken.getType());

//noinspection unchecked

adapter = (TypeAdapter>) 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 result = adapter.fromJson(json);

value.close();

return result.getData();

}

// 省略部分ParameterizedTypeImpl代码

}

我们首先看convert方法,首先获得ResponseBody 的值,用gson解析,第一次是判断code,如果服务端返回的不是成功的状态码,则抛出自定义的ApiException,该异常可以在DisposableObserver类的onError方法接收到;如果服务端返回的是成功的状态码,则对数据进行解析,最终返回data。

另外,ParameterizedTypeImpl是一个静态内部类(后面源码会放出),将基本泛型类型指定为BaseResponse,也就是说,原本

Observable> getArticleList(@Path("pageIndex") int pageIndex);

现在可以写成

Observable getArticleList(@Path("pageIndex") int pageIndex);

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

自定义DisposableObserver

public abstract class BaseResourceObserver extends DisposableObserver {

/*========================= 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;

}

}

替换我们自定义的Converter

Retrofit retrofit = new Retrofit.Builder()

.client(builder.build())

.baseUrl(ApiService.BASE_URL)

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

//.addConverterFactory(GsonConverterFactory.create())

//这里是自定义的GsonConverterFactory

.addConverterFactory(MyConverterFactory.create())

.build();

在activity请求,

Disposable disposable = ApiServiceImpl.getInstance()

.getArticleList(pageIndex)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

// 这里使用的是subscribeWith操作符

.subscribeWith(new BaseResourceObserver() {

@Override

public void onNext(ArticleBean articleBean) {

// do something

}

});

在subscribeWith里面初始化我们的BaseResourceObserver,只需重写onNext方法即可,简单便捷。

OK,至此处理json解析异常算是完成了。

总结

1、请求成功基本数据类型一般是不变的;

2、请求失败,只解析code错误码,不解析data,这样就不会出现解析异常了。

最后附上simple源码 RetrofitConverterSimple

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值