秒懂Retrofit2之Converter

【版权申明】非商业目的注明出处可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89675797
出自:shusheng007

系列文章
秒懂的Retrofit2源码详解
用Retrofit+RxJava2封装优雅的网络请求框架
秒懂Retrofit2之GsonConverter

概述

Retrofit2 已经成为Android开发中网络请求方面当之无愧的扛把子了,我们很有必要对自己经常使用的东西有个较为深入的理解。

Retrofit2 中有两个非常精彩的设计:ConverterCallAdapter, 通过这两个接口,Retrofit2的可扩展性被极大的增强了,用户可以根据需求自由扩展。我们这篇文章就对Converter做一个比较深入的解析。

作用

例如我们有如下代码片段:其中UserPerson 是两个自定义类

    @POST
    Observable<List<User>>method1(@Body Person rBody);

那么retrofit2是怎么认识我们自定义的类呢?这就是Converter 要干的事情。

下面是 Converter接口的源代码:

//将F类型的值转化为T类型的值
public interface Converter<F, T> {
  T convert(F value) throws IOException;
  
  abstract class Factory {
    //将API方法的返回类型从ResponseBody 转换为type,type是由CallAdapter 接口里面的responseType()函数返回的。
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    //将API方法的输入参数类型从 type转换为ResponseBody , 用于转换被注解@Body, @Part 和 @PartMap标记的类型
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    //将API方法的输入参数类型从 type 转换为String,用于转换被注解 @Header, @HeaderMap, @Path, @Query 和 @QueryMap 标记的类型
    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
    
    //下面是两个工具方法
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

通过其源码可见其只有一个方法 T convert(F value),就是将F类型的value转换为T类型的输出值。不同的Converter可以通过其内部类Factory得到。

使用原理

那么converter是如果作用于retrofit2的呢? 我们可以在retrofit2的源码中搜索 T convert(F value)的引用,如下图所示.我们可以发现 Converter的使用只有两种场景:

第一:红框中,使用Converter 将我们使用okhttp3 这个库发起HTTP请求的返回值的类型(RequestBody)转换为我们自定义的类型(T)。
针对我们上面的示例代码为:将RequestBody类型转换为List<User>类型

第二:蓝框中,使用Converter 将我们的输入参数类型从自定义类型转换为RequestBody 或者String
针对我们上面的示例代码为:将Person类型转换为RequestBody 类型
在这里插入图片描述
因为retrofit2在代码中是针对Converter 这个接口编程的,所以我们就可以为这个接口提供很多种具体的实现。Retrofits2中有一个默认的实现BuiltInConverters,让我们来看一下:

final class BuiltInConverters extends Converter.Factory {
  //做的工作很简单,这里要求方法返回值类型type必须为ResponseBody或者Void,转化后的类型为ResponseBody。
  //返回相应的Converter实例,其他的类型都处理不了,直接返回null
  //当方法返回值类型type是ResponseBody时检查一下方法是否使用了@Streaming注解标识,如果没有标识则将数据全部读取到内存中,返回一个ResponseBody
  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    if (type == ResponseBody.class) {
      return Utils.isAnnotationPresent(annotations, Streaming.class)
          ? StreamingResponseBodyConverter.INSTANCE
          : BufferingResponseBodyConverter.INSTANCE;
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    return null;
  }

  //更简单,要求方法的请求参数type必须为RequestBody类型,得到的类型也是RequestBody,没有做任何类型转换,
  //返回相应的Converter实例,其他的类型都处理不了,直接返回null
  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
      return RequestBodyConverter.INSTANCE;
    }
    return null;
  }

  //将object转成String
  static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();
    @Override public String convert(Object value) {
      return value.toString();
    }
  }
  ...

 static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
    static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();
    @Override public Void convert(ResponseBody value) {
      value.close();
      return null;
    }
  }

  static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
    static final RequestBodyConverter INSTANCE = new RequestBodyConverter();
    @Override public RequestBody convert(RequestBody value) {
      return value;
    }
  }

  //如果使用Streaming注解标记了方法,则使用这个转换器直接返回responseBody类型的数据,不读取到内存
  static final class StreamingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();
    @Override public ResponseBody convert(ResponseBody value) {
      return value;
    }
  }

  //如果不使用Streaming注解标记方法,则将返回值读取到内存中
  static final class BufferingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();
    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      try {
        // Buffer the entire body to avoid future I/O.
        return Utils.buffer(value);
      } finally {
        value.close();
      }
    }
  }
}

上面代码非常简单,在其中关键的两个方法上我也加了很详细的注释,没有太多可以解释的。其中值得注意的是那个stringConverter,由于没有对入参的依赖,所以没有采用实现Converter.Factory里面的

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

这种方式来实现。

通过以上源码我们可以解释很多Retrofit2的行为,例如
1:如果不添加自定义Converter,我们在定义方法时方法的入参的类型只能是RequestBody或者String,而返回值的泛型参数类型也只能是RequestBody 或者Void的现象。
2:如果不使用@Stream 注解标识方法,那么下载大文件时会发生OOM的问题,因为其会将返回数据一次性载入内存中。

如果是仅仅使用默认的Converter的话Retrofit2的使用将会受到极大的限制,但大神们是不允许这样的事发生的,所以我们可以自由使用各种Converter来满足我们的需求,再一次感受到了面向抽象编程的强大。

日常开发中最为常用的就是GsonConverter,但是现实中绝不仅仅只有GsonConverter,还有guava、jackson、java8、jaxb、moshi、protobuf、scalars、simplexml、wire 等Converter,如果别人写好的Converter不能满足我们的需求,那我们就需要自己写一个。

关于GsonConverter 请查看秒懂Retrofit2之GsonConverter

总结

可见一个设计优良的类库,会给用户很大的扩展空间,这一点我们应该多加学习。

下一篇讲下CallAdapter

发布了88 篇原创文章 · 获赞 279 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/ShuSheng0007/article/details/89675797