Android使用Retrofit实现自定义Converter解析接口流程详解

作者:萌动小彩笔 时间:2022-12-30 23:19:38 

不知道你们在使用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获取successfalse的情况下返回的数据:

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的修改就基本完成了,但是我们要使用它的时候还需要在自定义MyGsonConverterFactoryMyGsonRequestBodyConverter两个类。

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方法中,这个时候我们就可以判断是否抛出的是我们自定义的异常,如果是的话,那我们就可以获得当前的msgcode值从而去进行一些操作:

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) {
                      //登录成功
                   }
               });

来源:https://blog.csdn.net/zl_china/article/details/78529105

标签:Android,Retrofit,Converter,接口
0
投稿

猜你喜欢

  • Android 三级NestedScroll嵌套滚动实践

    2022-11-12 07:45:21
  • Monkeyrunner 常用按键总结

    2022-08-23 09:17:17
  • Spring实例化bean的四种方式详解

    2022-01-19 23:09:15
  • C#合并BitMap图像生成超大bitmap

    2023-12-08 18:24:11
  • Spring Data JPA中 in 条件参数的传递方式

    2023-11-08 06:45:47
  • java中File类的使用方法

    2023-09-11 11:49:55
  • J2EE Servlet上传文件到服务器并相应显示功能的实现代码

    2023-12-05 16:36:14
  • golang实现java uuid的序列化方法

    2022-12-30 11:30:57
  • Android自定义view之围棋动画效果的实现

    2022-05-07 17:36:38
  • Unity3D制作序列帧动画的方法

    2022-07-04 05:45:33
  • 一文教你如何使用Databinding写一个关注功能

    2023-09-17 12:26:47
  • Java实现单向链表反转

    2023-11-18 01:03:11
  • Java如何实现压缩文件与解压缩zip文件

    2022-01-28 09:14:00
  • Kotlin中的惰性操作容器Sequence序列使用原理详解

    2023-10-01 14:21:55
  • Android RecyclerView添加上拉加载更多功能

    2022-08-02 00:03:51
  • Spring Boot项目如何同时支持HTTP和HTTPS协议的实现

    2023-11-19 19:57:05
  • C#中Linq查询基本操作使用实例

    2023-11-25 00:25:37
  • SpringBoot实现文件上传下载功能小结

    2022-11-09 07:40:07
  • C#获取程序文件相关信息的方法

    2021-12-12 05:34:51
  • 一文彻底搞懂Kotlin中的协程

    2021-09-19 19:16:01
  • asp之家 软件编程 m.aspxhome.com