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
获取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) {
//登录成功
}
});
来源:https://blog.csdn.net/zl_china/article/details/78529105


猜你喜欢
- 问题背景实际项目碰到一个上游服务商接口有10秒的查询限制(同个账号)。项目中有一个需求是要实时统计一些数据,一个应用下可能有多个相同的账号。
- 在项目开发中,经常碰到map转实体对象或者对象转map的场景,工作中,很多时候我们可能比较喜欢使用第三方jar包的API对他们进行转化,而且
- 下面的示例提供对某个已存档类型的基本概述。示例// If compiling from the command line, compile
- 平时开发中经常遇到的很小的问题,这里记录一下。一般在AndroidManifest.xml中添加了android:windowSoftInp
- 本文实例讲述了C#访问SqlServer设置链接超时的方法。分享给大家供大家参考。具体实现方法如下:下面这段代码设置超时时间为60秒,默认为
- Compose中我们应该怎么使用地图呢?像之前我们在xml里面创建MapView,都是在Activity里面,管理MapView生命周期,和
- 在Struts2中Action部分,也就是Controller层采用了低侵入的方式。为什么这么说?这是因为在Struts2中action类并
- CyclicBarrier是什么CyclicBarrier是Java并发包中提供的一种同步工具类,它可以让多个线程在某个屏障处等待,直到所有
- 有时,通过Runtime.getRuntime().exec()执行命令的有效负载有时会失败。使用Web Shell,反序列化利用或通过其他
- 1 读取操作系统和CLR的版本OperatingSystem os = System.Environment.OSVersion; Cons
- 1 简介AOP,即面向切面编程是很常用的技术,特别是在Java Web开发中。而最流行的AOP框架分别是Spring AOP和AspectJ
- 关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了。Plupload的上传原理简单点说
- 本文实例讲述了Android 开发使用PopupWindow实现加载等待界面功能。分享给大家供大家参考,具体如下:实现加载等待界面我用了两种
- 本文实例讲述了Android持久化技术之SharedPreferences存储。分享给大家供大家参考,具体如下:1、SharedPrefer
- 一、本地仓库初始化与远程仓库推送操作Idea 基本环境配置Github 配置Git 执行文件目录指定创建工程git02创建本地仓库并提交项目
- 本文实例讲述了微信js sdk invalid signature签名错误问题的解决方法。分享给大家供大家参考,具体如下:/**最近在做微信
- 前面的文章使用sharding-jdbc实现水平分表中详细记录了如何使用sharding-jdbc实现水平分表,即根据相应的策略,将一部分数
- @RequestBody配合@Valid校验入参参数自定义一个Controllerimport com.example.demo.pojo.
- 前言数据库存储是我们常用的存储方式之一,对大批量数据有增、删、改、查操作需求时,我们就会想到使用数据库,Flutter中提供了一个sqfli
- 传播行为定义关于客户端和被调用方法的事务边界。Spring定义了7种截然不同的传播行为。1,PROPAGATION_MANDATORY,表示