0
点赞
收藏
分享

微信扫一扫

Retrofit - 自定义ConverterFactory (一)

深夜瞎琢磨 2021-09-29 阅读 60

转自:
Retrofit 2.0 自定义Converter
本文原创作者:一叶飘舟 作者博客地址:http://blog.csdn.net/jdsjlzx


在Retrofit中,无论是发送数据还是接收数据,最后,都是通过OkHttp的RequestBody或者ResponseBody来实现的,在实际项目中,有时候,原始的ReqeustBody或者ResponseBody无法满足我们的需求(比如: 接口需要加解密的时候),这时,就需要自定义ConverterFactory.

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

在Retrofit通过build()构造者模式,得到Retroft实例时,可以通过该addConverterFactory添加多个ConverterFactory,但是,这里需要注意的是,添加到顺序是有影响程序运行的,见下面代码

.addConverterFactory(GsonConverterFactory.create())

源码:

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

在nextRequestBodyConverter(...) 方法 和 nextResponseBodyConver(...)方法在,可以看到:
按照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;
      }
    }

从上面源码中可见,当factofy.requestBodyConvert(...)返回null时,表示没有匹配上,for循环会取出下一个factofy,因此,我们自定义ConverterFactory时候,要进行条件判断,符合我们的一定规则,才能使用,

所以说,如果你有2个ConverterFactory,都可以处理数据,那么,先添加的会生效

public interface Converter<F, T> {
  T convert(F value) throws IOException;

  abstract class Factory {

    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
  }
}

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

自定义ConverterFactory

我们以创建适配ProtoBuff协议的ConvertFactory为例

protobuf协议是以一个 .proto 后缀的文件为基础,这个文件描述了存在哪些数据,数据类型是怎么样的。

比json格式的数据,xml格式的数据 性能更好,效率更高
一条消息数据,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二进制序列化的10分之一(极端情况下,会大于等于直接序列化),总体看来ProtoBuf的优势还是很明显的

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<>();
  }
}

注意在Convert 接口的两个泛型中,前一个类型是传进来的对象类型,后一个类型是转换后的对象类型。
也就是说Convert 方法,要实现的内容,就是吧 前一个类型T -> F 的操作

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);
  }
}
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();
    }
  }
}

小结:

自定义ConverterFactory的几个关键如下:

  • 实现ConverterFactory 里的 两个方法
  • 方法内,根据方法参数,判断是否要处理,不处理的话,要返回null,以便retrofit能从ConverterFactofy列表里取下一个Factory实例
  • Convert接口的两个泛型,要区分好,哪个是in 哪个是out
举报

相关推荐

0 条评论