RxJava2 + Retrofit2 完全指南 之 统一状态码/Exception解决

作者 : 开心源码 本文共7358个字,预计阅读时间需要19分钟 发布时间: 2022-05-12 共187人阅读

前言

直接上数据结构:

{    "code": 200,    "data": {        "id": "1",        "name": "name1",        "stargazers_count": 1    },    "msg": "请求成功"}

上面的数据结构是一般比较简单而常见的数据结构,在正确的情况下我们只关心data里面的数据,错误的情况下我们关心codemsg提醒,而区分这两种情况又要不断的写大量的样板代码,这不是首选。所以就有了两种方法:

  • 通过定义泛型T来解决
  • 自己设置ResponseBodyConverter实现统一解决

首先讲讲泛型T的解决方式,基本如下:

public class BaseDataModel<T> {    private int code;    private T data;    private String msg;    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }    public T getData() {        return data;    }    public void setData(T data) {        this.data = data;    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public boolean isSuccessful() {        return code == 200;    }}

使用也比较简单,只要要将泛型替换为相应的实体类就行。但是这也有一个不好的地方就是一旦项目中对接的数据结构变得繁杂的时候,就需要不断的顶部相应的实体了,还是比较麻烦,后面会讲到,所以我们还是使用自己设置ResponseBodyConverter实现统一解决。

实现

分析

我在使用Retrofit的时候,一般是使用的是GsonConverterFactory,而GsonConverterFactory则是负责将我们的提交的数据进行序列化和将返回的数据进行反序列化的,所以只需我们分析完成其中的代码,看明白他是怎样对数据进行反序列化的,那么我们就能做到统一的数据转换了。

源码目录

GsonConverter源码目录

从上图我们可以看到,其实只有三个类,分别是:GsonConverterFactory、GsonRequestBodyConverter和GsonResponseBodyConverter。

  • GsonConverterFactory 主入口,负责联通GsonRequestBodyConverter和GsonResponseBodyConverter
  • GsonRequestBodyConverter 负责将注解为@Body的对象序列化为Json字符串。
  • GsonResponseBodyConverter 负责将返回的json字符串反序列化为对象。

其中GsonResponseBodyConverter的源码内容如下:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {  private final Gson gson;  private final TypeAdapter<T> adapter;  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {    this.gson = gson;    this.adapter = adapter;  }  @Override public T convert(ResponseBody value) throws IOException {    // 这里就是对返回结果进行解决    JsonReader jsonReader = gson.newJsonReader(value.charStream());    try {      T result = adapter.read(jsonReader);      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {        throw new JsonIOException("JSON document was not fully consumed.");      }      return result;    } finally {      value.close();    }  }}

从上面的源码可以看到,convert方法中其实就是通过Gson进行看一次序列化而已,而ResponseBody提供了string方法,直接将返回结果转换为了字符串,那么我们即可以在中间加一道工序,将其中的数据进行相关的判断解决,再进行返回。

编码

首先将GsonConverter的源码copy过来,而后改个名字,如下:

HandlerErrorGsonConvert

为了模拟不同的数据接口,硬编码了以下三种数据结构。

  • 数据结构·1
{    "code": 200,    "message": "成功,但是没有数据",    "data": []}
  • 数据结构·2
{    "code": -1,    "message": "这里是接口返回的:错误的信息,抛出错误信息提醒!",    "data": []}
  • 数据结构·3
{    "code": 401,    "message": "这里是接口返回的:权限不足,请重新登录!",    "data": []}

从上面三种数据结构分析,我们需要在code200的情况下直接序列化data,其它情况下抛出codemsg,所以这里我们还需要定义一个Exception类来承载错误信息。

创立Exception类

Exception类如下,主要是对少量和后台商定的状态码进行转义和包裹。

public class NetErrorException extends IOException {    private Throwable exception;    private int mErrorType = NO_CONNECT_ERROR;    private String mErrorMessage;    /*无连接异常*/    public static final int NoConnectError = 1;    /**     * 数据解析异常     */    public static final int PARSE_ERROR = 0;    /**     * 无连接异常     */    public static final int NO_CONNECT_ERROR = 1;    /*网络连接超时*/    public static final int SocketTimeoutError = 6;    /**     * 无法连接到服务     */    public static final int ConnectExceptionError = 7;    /**     * 服务器错误     */    public static final int HttpException = 8;    /**     * 登陆失效     */    public static final int LOGIN_OUT = 401;    /**     * 其余     */    public static final int OTHER = -99;    /**     * 没有网络     */    public static final int UNOKE = -1;    /**     * 无法找到     */    public static final int NOT_FOUND = 404;    /*其余*/    public NetErrorException(Throwable exception, int mErrorType) {        this.exception = exception;        this.mErrorType = mErrorType;    }    public NetErrorException(String message, Throwable cause) {        super(message, cause);    }    public NetErrorException(String message, int mErrorType) {        super(message);        this.mErrorType = mErrorType;        this.mErrorMessage = message;    }    @Override    public String getMessage() {        if (!TextUtils.isEmpty(mErrorMessage)) {            return mErrorMessage;        }        switch (mErrorType) {            case PARSE_ERROR:                return "数据解析异常";            case NO_CONNECT_ERROR:                return "无连接异常";            case OTHER:                return mErrorMessage;            case UNOKE:                return "当前无网络连接";            case ConnectExceptionError:                return "无法连接到服务器,请检查网络连接后再试!";            case HttpException:                try {                    if (exception.getMessage().equals("HTTP 500 Internal Server Error")) {                        return "服务器发生错误!";                    }                } catch (Exception e) {                    e.printStackTrace();                }                if (exception.getMessage().contains("Not Found"))                    return "无法连接到服务器,请检查网络连接后再试!";                return "服务器发生错误";        }        try {            return exception.getMessage();        } catch (Exception e) {            return "未知错误";        }    }    /**     * 获取错误类型     */    public int getErrorType() {        return mErrorType;    }}

统一订阅解决

因为这里使用的是RxJava,需要自己设置一个Subscriber来对Convert抛出的Exception进行捕获,也需要对其它Exception进行捕获和包裹,防止发生错误后直接崩溃,代码不多,如下:

public abstract class ApiSubscriber<T> extends ResourceSubscriber<T> {    @Override    public void onError(Throwable e) {        NetErrorException error = null;        if (e != null) {            // 对不是自己设置抛出的错误进行解析            if (!(e instanceof NetErrorException)) {                if (e instanceof UnknownHostException) {                    error = new NetErrorException(e, NetErrorException.NoConnectError);                } else if (e instanceof JSONException || e instanceof JsonParseException) {                    error = new NetErrorException(e, NetErrorException.PARSE_ERROR);                } else if (e instanceof SocketTimeoutException) {                    error = new NetErrorException(e, NetErrorException.SocketTimeoutError);                } else if (e instanceof ConnectException) {                    error = new NetErrorException(e, NetErrorException.ConnectExceptionError);                } else {                    error = new NetErrorException(e, NetErrorException.OTHER);                }            } else {                error = new NetErrorException(e.getMessage(), NetErrorException.OTHER);            }        }        // 回调笼统方法        onFail(error);    }    /**     * 回调错误     */    protected abstract void onFail(NetErrorException error);    }

修改HandlerErrorGsonResponseBodyConverter

这一步其实没什么难度,只是在convert方法中提前将ResponseBody.string()取出来,通过最简单的JSONObject来判断code,为200则返回data,否则抛出自己设置错误。详细代码如下:

final class HandlerErrorGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {    private final TypeAdapter<T> adapter;    /**模拟的假数据*/    private final List<String> mockResult;    private final Random random;    HandlerErrorGsonResponseBodyConverter(TypeAdapter<T> adapter) {        this.random = new Random();        this.adapter = adapter;        mockResult = new ArrayList<>();        mockResult.add("{\"code\":200,\"message\":\"成功,但是没有数据\",\"data\":[]}");        mockResult.add("{\"code\":-1,\"message\":\"这里是接口返回的:错误的信息,抛出错误信息提醒!\",\"data\":[]}");        mockResult.add("{\"code\":401,\"message\":\"这里是接口返回的:权限不足,请重新登录!\",\"data\":[]}");    }    @Override    public T convert(ResponseBody value) throws IOException {        // 这里就是对返回结果进行解决        String jsonString = value.string();        try {            // 这里为了模拟不同的网络请求,所以采用了本地字符串的格式而后进行随机选择判断结果。            int resultIndex = random.nextInt(mockResult.size() + 1);            if (resultIndex == mockResult.size()) {                return adapter.fromJson(jsonString);            } else {                // 这里模拟不同的数据结构                jsonString = mockResult.get(resultIndex);                Log.e("TAG", "这里进行了返回结果的判断");                // ------------------ JsonObject 只做了初略的判断,具体情况自定                JSONObject object = new JSONObject(jsonString);                int code = object.getInt("code");                if (code != 200) {                    throw new NetErrorException(object.getString("message"), code);                }                return adapter.fromJson(object.getString("data"));            }        } catch (JSONException e) {            e.printStackTrace();            throw new NetErrorException("数据解析异常", NetErrorException.PARSE_ERROR);        } finally {            value.close();        }    }}

如何调用

主要是有以下两个地方:

  1. 创立Retrofit的时候将addConverterFactory换成自己设置的HandlerErrorGsonConverterFactory.create()
  2. RxJava的subscribeWith换成自己设置的ApiSubscriber<T>。

部分代码如下:

......retrofit = new Retrofit.Builder()                .baseUrl("https://api.github.com/")                .addConverterFactory(HandlerErrorGsonConverterFactory.create()) // 这里使用的是用自己自己设置的转换器                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())                .build();service = retrofit.create(GitHubService.class);......mResultTv.setText("创立请求................\n");disposable = service.listRxJava2FlowableRepos("aohanyao", "owner")    .subscribeOn(Schedulers.io())    .observeOn(AndroidSchedulers.mainThread())    .subscribeWith(new ApiSubscriber<List<Repo>>() {        @Override        public void onNext(List<Repo> repos) {            mResultTv.append("请求成功,repoCount:" + repos.size() + ":\n");            for (Repo repo : repos) {                mResultTv.append("repoName:" + repo.getName() + "    star:" + repo.getStargazers_count() + "\n");            }        }        @Override        protected void onFail(NetErrorException error) {            mResultTv.append("请求失败" + error.getMessage() + "................\n");        }        @Override        public void onComplete() {            mResultTv.append("请求成功................\n");        }    });

演示

HandlerResponseError

结束

这里只写了对返回状态码和错误的统一解决,并未对不同的数据结构进行解决,下一篇将对不同的数据结构进行统一解决。

源码在这里

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » RxJava2 + Retrofit2 完全指南 之 统一状态码/Exception解决

发表回复