【版权申明】非商业目的注明出处可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89675797
出自:shusheng007
系列文章
秒懂的Retrofit2源码详解
用Retrofit+RxJava2封装优雅的网络请求框架
秒懂Retrofit2之GsonConverter
概述
Retrofit2 已经成为Android开发中网络请求方面当之无愧的扛把子了,我们很有必要对自己经常使用的东西有个较为深入的理解。
Retrofit2 中有两个非常精彩的设计:Converter 与CallAdapter, 通过这两个接口,Retrofit2的可扩展性被极大的增强了,用户可以根据需求自由扩展。我们这篇文章就对Converter做一个比较深入的解析。
作用
例如我们有如下代码片段:其中User
和 Person
是两个自定义类
@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请求的返回值的类型(ResponseBody)转换为我们自定义的类型(T)。
针对我们上面的示例代码为:将ResponseBody
类型转换为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
,而返回值的泛型参数类型也只能是ResponseBody
或者Void
。
感谢 【人的立方】同学的提醒,这块做了修正
2:如果不使用@Stream
注解标识方法,那么下载大文件时会发生OOM的问题,因为其会将返回数据一次性载入内存中。
如果是仅仅使用默认的Converter的话Retrofit2的使用将会受到极大的限制,但大神们是不允许这样的事发生的,所以我们可以自由使用各种Converter来满足我们的需求,再一次感受到了面向抽象编程的强大。
日常开发中最为常用的就是GsonConverter,但是现实中绝不仅仅只有GsonConverter,还有guava、jackson、java8、jaxb、moshi、protobuf、scalars、simplexml、wire 等Converter,如果别人写好的Converter不能满足我们的需求,那我们就需要自己写一个。
关于GsonConverter 请查看秒懂Retrofit2之GsonConverter
总结
可见一个设计优良的类库,会给用户很大的扩展空间,这一点我们应该多加学习。
下一篇讲下CallAdapter
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/14753.html