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

  不知道你们在使用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解析接口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android使用Retrofit上传文件功能

    本文实例为大家分享了Android使用Retrofit上传文件的具体代码,供大家参考,具体内容如下 一.封装RetrofitManager public class RetrofitManager {     private static RetrofitManager retrofitManager;          private Retrofit retrofit;     private RetrofitManager() {}     public static RetrofitMa

  • Android开发Retrofit源码分析

    目录 项目结构 retrofit 使用 Retrofit #create ServiceMethod #parseAnnotations HttpServiceMethod#parseAnnotations 第二种 非Kotlin协程情况 DefaultCallAdapterFactory#get 第一种 Kotlin协程情况 总结 项目结构 把源码 clone 下来 , 可以看到 retrofit 整体结构如下 图 http包目录下就是一些http协议常用接口 , 比如 请求方法 url ,

  • Android网络访问之Retrofit使用教程

    目录 一.概念 二.使用 2.1HttpURLConnection 2.2OKHttp 2.3Retrofit 2.3.1 定义实体类 2.3.2 定义API接口 2.3.3 构建Retrofit对象 2.3.4 创建API接口实例并调用访问函数 2.3.5 优化 一.概念 HttpClient Android 6中移除(API数量多扩展困难). HttpURLConnection 目前官方集成的. OKHttp Square公司出品,底层通讯的实现. Retrofit Square公司出品,上

  • Android Retrofit框架的使用

    Retrofit介绍 Retrofit是Square开源的一款基于OkHttp(也是他家的)封装的网络请求框架,主要的网络请求还是OkHttp来完成,Retrofit只是对OkHttp进行了封装,可以让我们更加简单方便的使用,目前大部分公司都在使用这款框架,Retrofit的原理也是面试必问的问题之一了,所以我们不仅要会使用,也要对其实现原理有一个大概的了解. 本片文章从使用角度来说,不对的地方希望大家在评论区交流,我会及时改进,共同进步,文章中的demo可以从github下载. Retrofi

  • Android Retrofit原理深入探索

    目录 序章 Retrofit构建过程 创建网络请求接口实例过程 执行请求过程 总结 序章 首先引入依赖 implementation 'com.squareup.retrofit2:retrofit:2.9.0' 在原理之前,我们先来回忆一下Retrofit的基本使用 1.定义接口 interface MyService { @GET("gallery/{imageType}/response") fun getImages(@Path("imageType") i

  • SpringBoot自定义路由覆盖实现流程详解

    目录 背景 设计 实现 注解定义 注解扫描及管理 自定义RequestMappingHandlerMapping 注册RequestMappingHandlerMapping 使用示例 背景 公司最近有一个项目二期需要对一些功能进行改造,涉及部分框架内置业务接口个性化定制,兼容老接口功能并且增加一部分新的数据返回,由于前端调用这些接口分布较多且较为零碎,修改测试成本较大,所以打算在框架层面提供路由覆盖功能,加快项目进度减少无技术含量的修改带来的系统风险 设计 提供自定义注解指定需要覆盖的路由及新

  • Android 自定义返回按钮的实例详解

    Android 自定义返回按钮的实例详解 程序中我们有时候想让放回按钮按照自己的需求调整页面而不是单纯的按照系统返回上一级,这个问题很简单,重写 onKeyDown 方法即可. 下面方法,包含了 webview 中的返回上一页和普通 activity 的单击设置和双击退出程序. @Override public boolean onKeyDown(int keyCode, KeyEvent event) { //如果我们用的是webview页面,想返回网页的上一页设置这里就可以了 if (key

  • Android Studio 生成自定义jar包的步骤详解

    想要将一个项目导出为jar包,供其它项目使用,在eclipse中可以直接导出该项目为jar包,而 在AS中可以通过修改gradle才处理. 接下来就介绍下具体的步骤: 1.新建一个项目,项目名随意,eg:MakeJarApplication,在项目中新建一个module类型为android-library ,命名为testLibrary.如图: 项目结构图 2.让app依赖这个库,在app下的build.gradle文件中添加compile project(':testlibrary') dep

  • Android自定义View实现体重表盘详解流程

    目录 效果视频 分析 起始角度 圆弧 指针 代码 初始化属性 画布 绘制内圆弧 绘制外圆弧 绘制中间指针 绘制中间文字 绘制左右两边文字 动画 全部代码 下载链接 效果视频 分析 起始角度 如下图所示,起点角度为150,终点角度为240 圆弧 白色圆弧为整个圆弧范围,蓝色圆弧为根据数据变动而覆盖白色圆弧,蓝色圆弧比白色圆弧大一点,突出显示 InnerArcPaint.setStrokeWidth( Width * (float)0.1 ); OuterArcPaint.setStrokeWidt

  • Android自定义view实现侧滑栏详解

    目录 前言 需求 效果图 编写代码 主要问题 前言 上一篇文章学了下自定义View的onDraw函数及自定义属性,做出来的滚动选择控件还算不错,就是逻辑复杂了一些.这篇文章打算利用自定义view的知识,直接手撕一个安卓侧滑栏,涉及到自定义LayoutParams.带padding和margin的measure和layout.利用requestLayout实现动画效果等,有一定难度,但能重新学到很多知识! 需求 这里类似旧版QQ(我特别喜欢之前的侧滑栏),有两层页面,滑动不是最左侧才触发的,而是从

  • Spring中自定义数据类型转换的方法详解

    目录 类型转换服务 实现Converter接口 实现ConverterFactory接口 实现GenericConverter接口 环境:Spring5.3.12.RELEASE. Spring 3引入了一个core.onvert包,提供一个通用类型转换系统.系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换.在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型.还可以在应用程序中需要类型转

  • Android开发之Kotlin委托的原理与使用详解

    目录 前言 一.接口/类委托 二.属性委托 三.延迟委托 四.观察者委托 五.Map委托 总结 前言 在设计模式中,委托模式(Delegate Pattern)与代理模式都是我们常用的设计模式(Proxy Pattern),两者非常的相似,又有细小的区分. 委托模式中,委托对象和被委托对象都是同一类型的对象,委托对象将任务委托给被委托对象来完成.委托模式可以用于实现事件监听器.回调函数等功能. 代理模式中,代理对象与被代理对象是两种不同的对象,代理对象代表被代理对象的功能,代理对象可以控制客户对

  • Spring Boot自定义错误视图的方法详解

    Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver).它基于一些常见的约定,尝试根据HTTP错误状态码解析出错误处理视图.它会在目录/error下针对提供的HTTP错误状态码搜索模板或者静态资源,比如,给定了HTTP状态码404,它会尝试搜索如下模板或者静态资源: /<templates>/error/404.<ext> - 这里<

  • Android view绘制流程详解

    绘制流程 measure 流程测量出 View 的宽高尺寸. layout 流程确定 View 的位置及最终尺寸. draw 流程将 View 绘制在屏幕上. Measure 测量流程 系统是通过 MeasureSpec 测量 View 的,在了解测量过程之前一定要了解这个 MeasureSpec . MeasureSpec MeasureSpec 是一个 32 位的 int 值打包而来的,打包为 MeasureSpec 主要是为了避免过多的对象内存分配. 为了方便操作,MeasureSpec

  • SpringBoot注入自定义的配置文件的方法详解

    目录 一.简介 二.代码实践 2.1 通过@value注解实现参数加载 2.2 通过@ConfigurationProperties注解实现参数加载 2.3 通过@PropertySource注解实现配置文件加载 2.4 通过自定义环境处理类,实现配置文件的加载 2.5 最后,我们来介绍一下yml文件读取 一.简介 在实际的项目开发过程中,我们经常需要将某些变量从代码里面抽离出来,放在配置文件里面,以便更加统一.灵活的管理服务配置信息.比如,数据库.eureka.zookeeper.redis.

随机推荐