Retrofit 2 0 自定义Converter

requestBodyConverter 不执行的解决办法:

参数要使用@Body这种形式,否则 request 方法会不起作用。

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

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

.addConverterFactory(GsonConverterFactory.create())
  • 1

看下源码:

private final List<Converter.Factory> converterFactories;

public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter.Factory factory = converterFactories.get(i);
      Converter<?, RequestBody> converter =
          factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<T, RequestBody>) converter;
      }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

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

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

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (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<ResponseBody, ?> 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<MessageLite> parser;
    try {
      Field field = c.getDeclaredField("PARSER");
      //noinspection unchecked
      parser = (Parser<MessageLite>) 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<?, RequestBody> 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 ProtoRequestBodyConverter<>();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

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

final class ProtoRequestBodyConverter<T extends MessageLite> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.parse("application/x-protobuf");

  @Override public RequestBody convert(T value) throws IOException {
    byte[] bytes = value.toByteArray();
    return RequestBody.create(MEDIA_TYPE, bytes);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
final class ProtoResponseBodyConverter<T extends MessageLite>
    implements Converter<ResponseBody, T> {
  private final Parser<T> parser;

  ProtoResponseBodyConverter(Parser<T> 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 {
      value.close();
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Annotation

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

我们先看requestBodyConverter的函数签名。

public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)
  • 1
  • 2

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

@GET("applist/mini-appcenter/")
    Call<MiniAppCenterPojo> getMiniApp(@Query("offsets") @TypeString String offsets);
  • 1
  • 2

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

@TypeString
    @GET("applist/mini-appcenter/")
    Call<MiniAppCenterPojo> getMiniApp(@Query("offsets") String offsets);
  • 1
  • 2
  • 3

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

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface TypeString{
}
  • 1
  • 2
  • 3
  • 4
  • 5
public class StringConverterFactory extends Converter.Factory {

    @Override
    public Converter<ResponseBody, ?> 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<?, RequestBody> 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<ResponseBody, String> {

        @Override
        public String convert(ResponseBody value) throws IOException {
            return value.string();
        }
    }

    public static class StringRequestConverter implements Converter<String, RequestBody> {
        @Override
        public RequestBody convert(String value) throws IOException {
            return RequestBody.create(MediaType.parse("application/octet-stream"), value);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47


下面再看一个基于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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new DecodeResponseBodyConverter<>(adapter);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new DecodeRequestBodyConverter<>(gson, adapter);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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

public class DecodeResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final TypeAdapter<T> adapter;

    DecodeResponseBodyConverter(TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        //解密字符串
        return adapter.fromJson(EncryptUtils.decode(value.string()));
    }
}

public class DecodeRequestBodyConverter<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;
    DecodeRequestBodyConverter(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.flush();
        return RequestBody.create(MEDIA_TYPE,buffer.readByteString());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

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

参考文章:

http://caiyao.name/2016/01/13/Retrofit%E4%BD%BF%E7%94%A8%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89Converter/

可能遇到的问题:

when required parameters have string and file together, retrofit 2.2.0 do not work

requestBodyConverter 不执行的解决办法:

参数要使用@Body这种形式,否则 request 方法会不起作用。

例如:

@POST("/index/index/sms_code")
Observable<BaseResopnse> sms_code(@Body String data);
  • 1
  • 2

下面我们看下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<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

原文地址:https://www.cnblogs.com/skiwnchhw/p/10349660.html

时间: 2024-10-10 09:44:28

Retrofit 2 0 自定义Converter的相关文章

Android Retrofit 2.0自定义JSONObject Converter

如果在使用的过程中,不需要Gson以及其他转换器,只是单纯的返回 JSONObject,那这样怎么处理呢? 通过阅读源码发现,可以通过自定义转换器的方式操作: import retrofit.Call /*Retrofit 2.0*/ public interfase ApiService{ @POST("/list") Call<JSONObject> loadRepo(); } 同步操作: Call<JSONObject> call = service.lo

Retrofit 2.0:迄今为止最大的更新最好的Android HTTP客户端库

前言 来自移动支付公司square公司的作品,开源世界top5的最小公司,首先我自己是一个忠实广场粉,okhttp.picasso.greendao.okio等等~ 据Square CTO Bob Lee的说法,Square已经将超过60个项目提交到开源社区,贡献了25万行左右的代码. 原文:Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android 因为其简单与出色的性能,Retrofit 是

这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)(转)

前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会喜欢. 如果对Retrofit v2.0的源码感兴趣,可看文章:Android:手把手带你深入剖析 Retrofit 2.0 源码 目录 1. 简介 特别注意: 准确来说,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装. 原因:网络请求的工作本质上是 OkHttp 完成,

Retrofit 2.0 使用详细教程

文章来自:https://blog.csdn.net/carson_ho/article/details/73732076 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会喜欢. 如果对Retrofit v2.0的源码感兴趣,可看文章:Android:手把手带你深入剖析 Retrofit 2.0 源码 目录 1. 简介 特别注意: 准确来说,Re

WatchOS2.0 自定义表盘元素

WatchOS2.0 自定义表盘元素 北京时间6月9日凌晨1点,苹果在美国旧金山举行了WWDC2015全球开发者大会发布新的WatchOS2.0操作系统,不仅与之前WatchOS1系统结构的改变,还添加许多新的功能.新特性及新UI控件,其中WatchOS2.0新Complications(自定义表盘元素)我认为是WatchOS2.0的亮点,好那么我们下面就来聊一聊这东西. 首先在WatchOS1的时候,只支持简单自定义配置表盘.watchOS2支持自定义表盘的Complication(苹果把表盘

Retrofit 2.0使用

最近在想能不能把之前项目里的网络请求改下 想通过Retrofit重构下,因为Retrofit完美兼容Rxjava后面会用到Rxjava 所以 开个坑写点 由于网上Retrofit 2.0的架构介绍很详细.所以我这里只提供我遇到的一些问题和解决的方法 首先要使用Retrofit进行网络请求,你必须建立一个请求接口类 ApiService public interface ApiService { /* @GET("service/getIpInfo.php") Call<GetIp

JSP2.0自定义标签

---| SimpleTag 接口 定义了标签处理类的生命周期方法.doTag() -----| SimpleTagSupport 类 全部实现了SimpleTag接口的方法,因此后面我们只需要继承并重写该类即可. 实现自己的if-.else标签 目标: 1 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 3 <c:choose> 4 5 <c:when

[Enterprise Library for .NET Framework 2.0]自定义日志路径或日志文件名称

有时候,日志输出的时候会根据时间来分类,譬如"20140821\trace.log",在Enterprise Library中通过工具配置,只能定义日志文件名称,可以通过代码修改FlatFileTraceListenerData实现或Custom Trace Listener方式, 通过代码修改FlatFileTraceListenerData实现代码如下: public static string GetTraceLogPath(string listenersName) { str

Vtiger6.0自定义表达式

首先,我说的自定义表达式是:我有一个字段A,和一个字段B.然后我的字段C=字段A+字段B,自动计算出来. 在网上找了一会,发现在vtigercrm6.0的自定义表达式表达式不见了. 这么好用的功能vtiger不可能移除掉. 这时候就体现出Google搜索的作用了,把vtiger5.4切换成英文自定义表达式的英文是"Field Formulas",用这个关键字搜索,你就能发现答案了. 原来,其实vtiger6.0为了更简洁,把自定义字段整合到工作流中了. 新建工作流->添加条件-&