美文网首页
Android 使用Retrofit自定义Converter解析

Android 使用Retrofit自定义Converter解析

作者: 极客天空 | 来源:发表于2020-08-27 09:49 被阅读0次

    一、简介

    在使用Retrofit访问后台接口时返回的数据是否是一样的格式,比如登录接口,在我们输入密码成功或错误的时候后台返回的数据格式是不同的,这样我们在添加GsonConverterFactory解析后台数据时由于后台会返回两种不同的数据所以会导致Gson解析失败的错误信息。这里以自己项目的登录接口为例子记录下自己的解决方案。

    二、问题

    登录成功和失败的两种数据格式:

    {"success":false,"code":-1,"msg":"密码错误","data":"密码错误"}
    
    {"success":true,"code":1,"msg":"操作成功","data":{"JXHDAPIToken":"1ccf7b01ed544882aacda365c8f620d2"}}
    

    三、解决思路

    从上面数据中我们可以发现后台返回的数据只有data中的数据是不一样,其他几个字段都是一样的。这个时候我们可以将几个相同字段抽取出来,通过修改GsonConverterFactory来实现解析不同数据。
    修改GsonConverterFactory:
    我们只需要点击进入GsonConverterFactory的源码中看看它的代码,这里面我们只需要修改返回的GsonResponseBodyConverter对象。

    @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonResponseBodyConverter<>(gson, adapter);
    

    所以我们再点击进入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 {
          return adapter.read(jsonReader);
        } finally {
          value.close();
        }
      }
    }
    

    我们是无法直接在GsonResponseBodyConverter里面直接进行修改的,所以我们可以自定义一个MyGsonResponseBodyConverter类:

    final class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody,T>{
        private Gson gson;
        private Type type;
    
        public MyGsonResponseBodyConverter(Gson gson, Type type) {
            this.gson = gson;
            this.type = type;
        }
    
        @Override
        public T convert(ResponseBody value) throws IOException {
            String response = value.string();
            try {
                BaseBean baseBean = gson.fromJson(response,BaseBean.class);
                if (!baseBean.isSuccess()) {
                    throw new DataResultException(baseBean.getMsg(),baseBean.getCode());
                }
                return gson.fromJson(JsonUtils.getData(response),type);
            }finally {
                value.close();
            }
        }
    }
    

    这里面我们通过自己抽取出来的BaseBean去判断当前接口返回的字段success,如果为true的情况下我们正常返回数据即可,为false的情况我们通过自定义DataResultException异常类将其抛出。

    public class BaseBean {
        private boolean success;
        private int code;
        private String msg;
    
        public boolean isSuccess() {
            return success;
        }
    
        public void setSuccess(boolean success) {
            this.success = success;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    

    通过DataResultException获取success为false的情况下返回的数据:

    public class DataResultException extends IOException {
        private String msg;
        private int code;
    
        public DataResultException(String msg, int code) {
            this.msg = msg;
            this.code = code;
        }
    
        public DataResultException(String message, String msg, int code) {
            super(message);
            this.msg = msg;
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    }
    

    这个时候我们对GsonConverterFactory的修改就基本完成了,但是我们要使用它的时候还需要在自定义MyGsonConverterFactory和MyGsonRequestBodyConverter两个类。

    public class MyGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");
    
        private final Gson gson;
        private final TypeAdapter<T> adapter;
    
        MyGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }
    
        @Override public RequestBody convert(T value) throws IOException {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = gson.newJsonWriter(writer);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
        }
    

    这里主要是为了将之前自定义的MyGsonResponseBodyConverter添加进去,而MyGsonRequestBodyConverter这个类和GsonRequestBodyConverter源码内容是一样的,我们直接将内容copy过来即可。

    public final class MyGsonConverterFactory extends Converter.Factory {
    
        public static MyGsonConverterFactory create() {
            return create(new Gson());
        }
    
        public static MyGsonConverterFactory create(Gson gson) {
            if (gson == null) throw new NullPointerException("gson == null");
            return new MyGsonConverterFactory(gson);
        }
    
        private final Gson gson;
    
        private MyGsonConverterFactory(Gson gson) {
            this.gson = gson;
        }
    
        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                                Retrofit retrofit) {
            return new MyGsonResponseBodyConverter<>(gson,type);
        }
    
        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new MyGsonRequestBodyConverter<>(gson, adapter);
        }
    }
    

    我们只需要将MyGsonConverterFactory替换之前的GsonConverterFactory即可:addConverterFactory(MyGsonConverterFactory.create())
    这个时候我们再去调用之前的登录接口,当我们登录失败的时候会进入onError方法中,这个时候我们就可以判断是否抛出的是我们自定义的异常,如果是的话,那我们就可以获得当前的msg和code值从而去进行一些操作:

    RetrofitUtils.getInstance().getApi().login()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Subscriber<LoginResult>() {
                        @Override
                        public void onSubscribe(Subscription s) {
    
                        }
    
                        @Override
                        public void onNext(LoginResult loginResult) {
    
                        }
    
                        @Override
                        public void onError(Throwable t) {
                         if (t instanceof DataResultException) {
                             DataResultException resultException = (DataResultException) t;
                             Log.d(TAG, "Code: " + resultException.getCode() + "Message:" + resultException.getMessage());
                           }
                        }
    
                        @Override
                        public void onComplete() {
    
                        }
                    });
    

    这里我们可以对Subscriber进行一下封装,要不我们每次请求都需要去进行判断显得很是繁琐:

    public abstract class MySubscriber<T> implements Subscriber<T> {
    
        @Override
        public void onSubscribe(Subscription s) {
            s.request(Long.MAX_VALUE);
        }
    
        @Override
        public void onComplete() {
    
        }
    
        @Override
        public void onError(Throwable t) {
            //这里面根据自己项目需求进行操作,比如code判断。
            if (t instanceof DataResultException) {
                DataResultException resultException = (DataResultException) t;
                Log.d(TAG, "Code: " + resultException.getCode() + "Message:" + resultException.getMessage());
            }
        }
    

    封装后我们只会调用onNext的方法,当然我们如果需要其他操作的话也可以将其他几个方法重写:

    RetrofitUtils.getInstance().getApi().login()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new MySubscriber<LoginResult>() {
                        @Override
                        public void onNext(LoginResult loginResult) {
                           //登录成功
                        }
                    });
    

    相关文章

      网友评论

          本文标题:Android 使用Retrofit自定义Converter解析

          本文链接:https://www.haomeiwen.com/subject/nbzqsktx.html