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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 每个App都避免不了要进行网络请求,从最开始的用谷歌封装的volley到再到android-async-http再到OKHttpUtils再到现在的Retrofit和RxJava,从我自己用后的体验来看,用了retrofit和RxJava真的回不去了。

每个App都避免不了要进行网络请求,从最开始的用谷歌封装的volley到再到android-async-http再到OKHttpUtils再到现在的Retrofit和RxJava,从我自己用后的体验来看,用了retrofit和RxJava真的回不去了,回不去了,不去了,去了,了…(哈哈,本来还想分析下这四个的区别,网上这样的文章很多,我就没必要多添乱了-.-)。不多逼逼,下面开始正文。

1、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日志

2、网络请求基类的配置

建立一个工厂类

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> 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的类型定义为一个泛型的

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 下面就定义我们的网络请求订阅subscriber

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做具体的操作
                }
            });
}

你以为这样就行了 , 这样子确实没毛病,确实已经妥妥的了。可是,可是,事与愿违啊!!!

3、具体解决办法

一般情况这是我们的返回json格式:

{
    "code":200,
        "msg":"成功",
        "result":{}
}

我们刚才定义登录接口的时候 返回的实体基类例传入的是UserInfoEntity 这确实是没问题的 可是你们加入登录失败的时候返回的json数据格式是这样的怎么办?

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

失败的时候返回的实体又是一个数组,这样子就会抱一个json解析异常拿不多失败的状态码和提示信息

OK其实我们的网络请求已经完成90%了,剩下的就是不重要的失败的时候回调了。

方法一:(这是在后台兄弟好说话,而且不打人的情况下…当然这种好人,还是有的,不过这不是我们今天要讲的重点)

我们可以让后台返回的json数据中的result永远是个数组。

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

方法二:

首先给大家看一个图片

这就是我们要下手的地方
上面我们添加的工厂变换器是导入的依赖 compile 'com.squareup.retrofit2:converter-gson:2.0.2' 这个提供的

那可能有人要问了,那我们不用这个用哪个啊,不着急,不着急。还好retrofit是支持自定义的ConverterFactory的

下面我们就开始我们的自定义征程吧。

1、自定义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());
        }
    }
}

2、自定义一个响应变换工厂 继承自 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 对于效率的影响其实并不大,在功能实现的基础上一点点效率的影响(而且这个影响是微乎其微的-.-)其实无伤大雅的。

原文发布时间为:2018-07-11
本文来自云栖社区合作伙伴“安卓巴士Android开发者门户”,了解相关信息可以关注“安卓巴士Android开发者门户”。

相关文章
|
1月前
|
消息中间件 存储 缓存
十万订单每秒热点数据架构优化实践深度解析
【11月更文挑战第20天】随着互联网技术的飞速发展,电子商务平台在高峰时段需要处理海量订单,这对系统的性能、稳定性和扩展性提出了极高的要求。尤其是在“双十一”、“618”等大型促销活动中,每秒需要处理数万甚至数十万笔订单,这对系统的热点数据处理能力构成了严峻挑战。本文将深入探讨如何优化架构以应对每秒十万订单级别的热点数据处理,从历史背景、功能点、业务场景、底层原理以及使用Java模拟示例等多个维度进行剖析。
55 8
|
1月前
|
数据采集 自然语言处理 搜索推荐
基于qwen2.5的长文本解析、数据预测与趋势分析、代码生成能力赋能esg报告分析
Qwen2.5是一款强大的生成式预训练语言模型,擅长自然语言理解和生成,支持长文本解析、数据预测、代码生成等复杂任务。Qwen-Long作为其变体,专为长上下文场景优化,适用于大型文档处理、知识图谱构建等。Qwen2.5在ESG报告解析、多Agent协作、数学模型生成等方面表现出色,提供灵活且高效的解决方案。
159 49
|
22天前
|
运维 数据库连接 PHP
PHP中的异常处理机制深度解析####
本文深入探讨了PHP中异常处理机制的工作原理,通过实例分析展示了如何有效地使用try-catch语句来捕获和处理运行时错误。我们将从基础概念出发,逐步深入到高级应用技巧,旨在帮助开发者更好地理解和利用这一强大的工具,以提高代码的稳定性和可维护性。 ####
|
22天前
|
PHP 开发者 UED
PHP中的异常处理机制解析####
本文深入探讨了PHP中的异常处理机制,通过实例解析try-catch语句的用法,并对比传统错误处理方式,揭示其在提升代码健壮性与可维护性方面的优势。文章还简要介绍了自定义异常类的创建及其应用场景,为开发者提供实用的技术参考。 ####
|
24天前
|
XML JSON JavaScript
HttpGet 请求的响应处理:获取和解析数据
HttpGet 请求的响应处理:获取和解析数据
|
25天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
1月前
|
数据库连接 PHP 开发者
PHP中的异常处理机制深度解析####
本文深入探讨了PHP中异常处理的核心概念、使用场景及最佳实践,旨在帮助开发者更高效地管理和响应运行时错误。通过实例演示和理论分析,揭示try-catch块的运作原理,以及如何自定义异常类以增强代码的可读性和可维护性。文章还对比了传统错误处理方式与异常处理的优势,为读者提供了在复杂项目中实施健壮错误管理策略的指导。 ####
|
1月前
|
API PHP 数据库
PHP中的异常处理机制深度解析与最佳实践####
本文深入探讨了PHP中异常处理机制的核心概念、工作原理及其在现代Web开发中的应用。通过剖析try-catch结构、自定义异常类及异常的继承体系,揭示了如何高效地捕获、处理并管理运行时错误,以提升应用的稳定性和用户体验。文章还结合实例,分享了在实际项目中实施异常处理的最佳实践,帮助开发者构建更加健壮的PHP应用程序。 ####
|
1月前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
1月前
|
数据库连接 PHP 开发者
PHP中的异常处理深度解析####
【10月更文挑战第29天】 本文深入探讨了PHP中的异常处理机制,通过实例演示如何有效地捕获和处理运行时错误,提升代码的健壮性和可维护性。我们将从基础概念出发,逐步深入到自定义异常类的应用,以及如何在复杂项目中实施最佳实践。 --- ###
46 4

热门文章

最新文章

推荐镜像

更多