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

 更新时间:2023年03月12日 10:36:55   作者:萌动小彩笔  
Retrofit是一个RESTful的HTTP网络请求框架的封装,网络请求的工作本质上是OkHttp完成,而Retrofit仅负责网络请求接口的封装

  不知道你们在使用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) {
                       //登录成功
                    }
                });

好了,到这里就结束了,不知不觉感觉时间过的好快,总感觉自己的时间不够用,感觉还有好多好多东西需要自己去学习但总是莫名的懒惰。哈哈,希望自己在以后的道路上能继续努力慢慢成长。

到此这篇关于Android使用Retrofit实现自定义Converter解析接口流程详解的文章就介绍到这了,更多相关Android自定义Converter解析接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 仿饿了吗点餐界面ListView联动的实现

    仿饿了吗点餐界面ListView联动的实现

    这篇文章主要介绍了仿饿了吗点餐界面ListView联动的实现的相关资料,本文介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09
  • 源码分析Android rinflate的使用

    源码分析Android rinflate的使用

    这篇文章主要将从源码的角度带大家一起分析Android rinflate的使用,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
    2023-04-04
  • 使用ViewPager2实现简易轮播图效果

    使用ViewPager2实现简易轮播图效果

    这篇文章主要为大家详细介绍了使用ViewPager2实现简易轮播图效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • Android实现美团下拉功能

    Android实现美团下拉功能

    这篇文章主要为大家详细介绍了Android实现美团下拉功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • 详解Flutter点击空白隐藏键盘的全局做法

    详解Flutter点击空白隐藏键盘的全局做法

    这篇文章主要介绍了详解Flutter点击空白隐藏键盘的全局做法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Android通过ImageView设置手指滑动控件缩放

    Android通过ImageView设置手指滑动控件缩放

    这篇文章主要介绍了Android通过ImageView设置手指滑动控件缩放效果,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-12-12
  • android中处理各种触摸事件的方法浅谈

    android中处理各种触摸事件的方法浅谈

    android中处理各种触摸事件的方法浅谈,需要的朋友可以参考一下
    2013-06-06
  • Android仿网易一元夺宝客户端下拉加载动画效果(一)

    Android仿网易一元夺宝客户端下拉加载动画效果(一)

    本文通过一个demo给大家介绍了android仿网易一元夺宝客户端下拉加载动画效果,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09
  • Android利用SAX对XML进行增删改查操作详解

    Android利用SAX对XML进行增删改查操作详解

    在项目中会遇到对于XML的增删改查,下面这篇文章主要给大家介绍了关于Android利用SAX对XML进行增删改查操作的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • Android 程序申请权限注意事项

    Android 程序申请权限注意事项

    本主要介绍Android 程序申请权限注意事项,这里整理了相关资料,并详细说明如何避免开发的程序支持设备减少,有需要的小伙伴可以参考下
    2016-09-09

最新评论