Retrofit 2.0 自定义Converter

网友投稿 945 2022-12-01

Retrofit 2.0 自定义Converter

Retrofit 2.0  自定义Converter

requestBodyConverter 不执行的解决办法: 参数要使用@Body这种形式,否则 request 方法会不起作用。

在Retrofit中,无论是发送数据和接收数据,都是通过OKHttp的RequestBody和ResponseBody来实现的。在实际项目中,有时候原始的RequestBody或是ResponseBody并不能满足我们的需求(如接口加密),就需要对它进行转换。

在Retrofit通过build()方法获得实例时,可以添加多个ConverterFactory,但要注意的是,添加的顺序是有影响的。如下代码:

.addConverterFactory(GsonConverterFactory.create())

看下源码:

private final List converterFactories;public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(checkNotNull(factory, "factory == null")); return this; }

按照retrofit的逻辑,是从前往后进行匹配,如果匹配上,就忽略后面的,直接使用。

int start = converterFactories.indexOf(skipPast) + 1; for (int i = start, count = converterFactories.size(); i < count; i++) { Converter.Factory factory = converterFactories.get(i); Converter converter = factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this); if (converter != null) { //noinspection unchecked return

从上面的源码中可以看到,当factory.requestBodyConverter返回空时,表示没有匹配上,可使用下一个factory.

因此,当我们自定义converter的时候,需要进行条件判断,符合我们一定规则的才能使用。

Retrofit官方给了以下几个常用的转换库

Gson: com.squareup.retrofit2:converter-gsonJackson: com.squareup.retrofit2:converter-jacksonMoshi: com.squareup.retrofit2:converter-moshiProtobuf: com.squareup.retrofit2:converter-protobufWire: com.squareup.retrofit2:converter-wireSimple XML: com.squareup.retrofit2:converter-simplexmlScalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

Type

我们以创建protobuff为例。

Retrofit已经为我们提供了自定义ConverterFactory的接口,我们只需要实现它给的接口即可。

public final class ProtoConverterFactory extends Converter.Factory public static ProtoConverterFactory create() { return new ProtoConverterFactory(); } @Override public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { //进行条件判断,如果传进来的Type不是class,则匹配失败 if (!(type instanceof Class)) { return null; } //进行条件判断,如果传进来的Type不是MessageLite的实现类,则也匹配失败 Class c = (Class) type; if (!MessageLite.class.isAssignableFrom(c)) { return null; } Parser parser; try { Field field = c.getDeclaredField("PARSER"); //noinspection unchecked parser = (Parser) field.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new IllegalArgumentException( "Found a protobuf message but " + c.getName() + " had no PARSER field."); } //返回一个实现了Converter的类, return new ProtoResponseBodyConverter<>(parser); } @Override public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { //进行条件判断,如果传进来的Type不是class,则匹配失败 if (!(type instanceof Class)) { return null; } //进行条件判断,如果传进来的Type不是MessageLite的实现类,则也匹配失败 if (!MessageLite.class.isAssignableFrom((Class) type)) { return null; } return new

注意在Convert的两个泛型中,前一个类型是传进来的对象类型,后一个类型是转换后的对象类型。

final class ProtoRequestBodyConverter implements Converter { private static final MediaType MEDIA_TYPE = MediaType.parse("application/x-protobuf"); @Override public RequestBody convert(T value) throws IOException { byte[] bytes = value.toByteArray(); return

final class ProtoResponseBodyConverter implements Converter { private final Parser parser; ProtoResponseBodyConverter(Parser parser) { this.parser = parser; } @Override public T convert(ResponseBody value) throws IOException { try { return parser.parseFrom(value.byteStream()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); // Despite extending IOException, this is data mismatch. } finally

Annotation

在有一些情况,仅仅通过type来进行判断,信息是不够的,还需要额外的参数。这时我们就可以利用后面两个参数来进行。

我们先看requestBodyConverter的函数签名。

public Converter requestBodyConverter(Type type,

显然,parameterAnnoatations是指在定义接口的参数上的注解。如下面的MyType:

@GET("applist/mini-appcenter/") Call getMiniApp(@Query("offsets") @TypeString String

定义在方法上的注释就是methodAnnotations

@TypeString @GET("applist/mini-appcenter/") Call getMiniApp(@Query("offsets");

我们就可以通过对这些注解的判断来进行自定义Converter的匹配。

@Documented@Target(PARAMETER)@Retention(RUNTIME)public @interface TypeString{

public class StringConverterFactory extends Converter.Factory @Override public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { if (!(type instanceof Class)) { return null; } for( Annotation annotation :annotations) { if( annotation instanceof TypeString) { return new StringResponseConverter(); } } return null; } @Override public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { if (!(type instanceof Class)) { return null; } for( Annotation annotation :parameterAnnotations) { if( annotation instanceof TypeString) { return new StringRequestConverter(); } } return null; } public static class StringResponseConverter implements Converter { @Override public String convert(ResponseBody value) throws IOException { return value.string(); } } public static class StringRequestConverter implements Converter { @Override public RequestBody convert(String value) throws IOException { return RequestBody.create(MediaType.parse("application/octet-stream"), value); } }}

下面再看一个基于Gson的自定义ConverterFactory。

如果我们对安全性要求比较高,或者编码不太一样的话,默认的GsonConverterFactory就不行了,我们就需要自定义ConverterFactory。

代码如下:

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

然后我们需要自定义responseBodyConverter和requestBodyConverter,这里我们发送请求的时候不需要加密,接收请求的时候需要解密,具体代码如下:

public class DecodeResponseBodyConverter implements Converter { private final TypeAdapter adapter; DecodeResponseBodyConverter(TypeAdapter adapter) { this.adapter = adapter; } @Override public T convert(ResponseBody value) throws IOException { //解密字符串 return adapter.fromjson(EncryptUtils.decode(value.string())); }}public class DecodeRequestBodyConverter implements Converter { 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 adapter; DecodeRequestBodyConverter(Gson gson,TypeAdapter 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.flush(); return

需要注意的是:adapter.fromJson(EncryptUtils.decode(value.string())) 中EncryptUtils.decode(value.string())返回类型必须是json字符串,否则会报错。

参考文章: ​​​​​​when required parameters have string and file together, retrofit 2.2.0 do not work ​​

requestBodyConverter 不执行的解决办法: 参数要使用@Body这种形式,否则 request 方法会不起作用。

例如:

@POST("/index/index/sms_code")Observable sms_code(@Body String

下面我们看下requestBodyConverter的源码:

/** * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if * {@code type} cannot be handled by this factory. This is used to create converters for types * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap} * values. */ public @Nullable Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return null; }

specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}从这里我们可以看到,除了@Body,@Part、@PartMap类型的参数也是可以的。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:关于AsyncHttpClient的cz.msebera.android.httpclient.Header
下一篇:如何使用 saplink 安装其他网站上提供的 ABAP 程序试读版
相关文章

 发表评论

暂时没有评论,来抢沙发吧~