Retrofit - 自定义ConverterFactory (一)

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


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

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

P.S. 笔者注: Google的GsonConverterFactory就是很好的自定义ConverterFactory的例子,我们可以阅读其源码,研究如何自定义ConverterFactory

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

.addConverterFactory(GsonConverterFactory.create())

源码:

    private List 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 converter =
          factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter) converter;
      }
    }

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

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

P.S. 笔者注:
这里说明下,为什么: 返回null,表示没匹配上,因为在抽象类Convert.Factory中(是所有自定义ConverFactory的基类),responseBodyConverter(...)/requestBodyConvertFactory(...) 默认是返回null的

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

  abstract class Factory {

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

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

    public Converter 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为例

P.S. 科普一下Proto Buffs
这是google开源的一个项目
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

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

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

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 RequestBody.create(MEDIA_TYPE, bytes);
  }
}
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 {
      value.close();
    }
  }
}

小结:

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

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

P.S. 作者的文章很长,我拆分成几个部分,免得看的时候失去耐心.

你可能感兴趣的:(Retrofit - 自定义ConverterFactory (一))