Retrofit之Converter详解
Retrofit具有很高的扩展性,得益于预留的丰富接口,开发者可以根据业务自行实现接口。以Retrofit中的结果转换器Converter为例,了解其实现流程,最后根据业务自定义一个Converter。
1、Converter
Converter接口中只定义了一个T convert(F value)接口方法,以及一个创建Converter实例的抽象工厂。Retrofit根据返回类型Type从各种Converter.Factory中获取对应的转换器。
public interface Converter<F, T> {
@Nullable
T convert(F value) throws IOException;
/** Creates {@link Converter} instances based on a type and target usage. */
abstract class Factory {...}
}Converter接口中的两个泛型参数<F,T>,含义分别为from和to。也就是从HTTP请求“来”的结果ResponseBody,以及需要“去”转换成的类型T(可以是自定义的Json Bean类,还可以是原始的ResponseBody)。
1.1、Converter.Factory初始化
Retrofit中通过Converter.Factory获取Converter实例。先来看看这些工厂是如何初始化的。
首先,在构造Retrofit实例的时候,通过建造者的addConverterFactory(Converter.Factory factory)方法添加自定义的Converter.Factory:
/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}最后在构造Retrofit实例的时候:先将内置转换器工厂BuiltInConverters添加到集合中;再将自定义的转换器工厂拷贝到converterFactories;最后,从Platform获取平台默认的转换器工厂。
public Retrofit build() {
...
// 构造1 + 自定义转换器 + 默认转换器 大小的集合
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// 首先添加内置转换器工厂。
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
...
}从上面可以看出,一个Retrofit实例中的Converter.Factory数量为:1(内建) + 自定义数量 + Platform默认数量。
1.2、通过工厂获取Converter实例
那么何时通过Converter.Factory创建Converter呢?在Retrofit源码流程分析一文第2.2节中创建HttpServiceMethod的parseAnnotations(Retrofit, Method, RequestFactory)方法中:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
//获取Converter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
...
}通过createResponseConverter(retrofit, method, responseType)方法创建Converter<ResponseBody,ResponseT>转换器。注意这里第一个泛型参数F已经限定为ResponseBody,后面自定义Converter的时候一定要留意。
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}上面的方法内部再通过Retrofit实例的responseBodyConverter(Type , Annotation[] annotations)方法获取转换器:
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}再使用nextResponseBodyConverter()按顺序遍历1.1节中给Retrofit添加的所有Converter.Factory。如果有对应的Converter.Factory可以处理并返回Converter,则直接返回。并且后面的Converter.Factory就跳过,所以Converter.Factory的添加顺序也会影响到获取的Converter类型。
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
...
//如果没有对应的转换器,那么会抛出异常
throw new IllegalArgumentException();
}如果没有能够处理指定返回类型Type的Converter,那么在最后抛出异常。
1.3、Converter转换请求响应
又在何时使用Converter呢?在Retrofit源码流程分析一文的第3.1节网络请求结果回调中看到了转换器的身影,Retrofit使用OkHttpCall封装okhttp3.Call,并且处理网络回调。
@Override
public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
...
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
response = parseResponse(rawResponse);
...
}
...
});
}parseResponse(rawResponse)方法显然是用来进行结果转换的。处理原始的ResponseBody,并转换成需要的类型返回(比如Json bean类或者其它数据结构)。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
...
//将OkHttp原始Response包装成ExceptionCatchingResponseBody
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {}
}这里用到的成员responseConverter就是1.2节中创建的Converter实例,还记得OkHttpCall是在哪里初始化的吗?
回到Retrofit源码流程分析一文的第2.3节中讲到的invoke(Object[] args)方法,将1.2节中创建的Converter实例作为参数构造OkHttpCall。
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}2、自定义Converter
Retrofit作为热门的网络请求库,附带很多转换器的实现,方便将网络请求直接转换成开发需要的类型。比如常用GsonConverterFactory内部使用Gson将网络请求结果转换成Json Bean类。
//Gson
implementation 'com.squareup.retrofit2:converter-gson'
//Jackson
implementation 'com.squareup.retrofit2:converter-jackson'
//Moshi
implementation 'com.squareup.retrofit2:converter-moshi'
//Protobuf
implementation 'com.squareup.retrofit2:converter-protobuf'
//Wire
implementation 'com.squareup.retrofit2:converter-wire'
//Simple Framework
implementation 'com.squareup.retrofit2:converter-simplexml'
//Scalars
implementation 'com.squareup.retrofit2:converter-scalars'这么多附加的转换器还是满足不了业务需求怎么办?自己动手丰衣足食,自定义Converter及Converter.Factory。前面已经对Converter的实现过程有一个全面的认识,接下来就是自定义一个转换器,将网络请求结果直接转换成Bitmap。
2.1、自定义Converter工厂
自定义转换器工厂很简单,只需要实现Converter.Factory抽象类,重写Converter<ResponseBody, ?> responseBodyConverter()方法:主要是根据返回类型Type判断能否处理,并且返回对应的Converter。如果无法处理直接返回null(父类默认返回null),Retrofit会继续遍历后面的Converter.Factory,直到找到一个能够处理的Converter。
class BitmapConverterFactory private constructor() : Converter.Factory() {
companion object {
private val INSTANCE = BitmapConverterFactory()
fun getInstance(): BitmapConverterFactory {
return INSTANCE
}
}
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
//如果返回类型是Bitmap,那么就返回自定义的Bitmap转换器。否则返回null。
return if (type.typeName == (Bitmap::class.java.name)) {
BitmapResponseBodyConverter()
} else null
}
}Converter.Factory还有另外一个Converter<?, RequestBody> requestBodyConverter()方法可以重写,可以对网络请求前的RequestBody拦截进行一些操作。在RequestFactory的parseParameterAnnotation(...)方法中解析注解参数构造请求,会通过Retrofit获取合适的转换器以处理请求RequestBody,流程和ResponseBody转换器类似,就不再赘述。
2.2、实现Converter
从前面的1.3节中,我们知道,Retrofit将网络请求响应体ResponseBody包装成ExceptionCatchingResponseBody给Converter处理。而且OkHttpCall中的转换器类型定义如下,Converter<F,T>中的from类型参数必须是ResponseBody:
private final Converter<ResponseBody, T> responseConverter;自定义Converter很简单,只需实现Converter接口。实现T convert(F value)方法,从而完成网络响应从F -> T的类型转换。
public interface Converter<F, T> {
T convert(F value) throws IOException;
}以自定义BitmapResponseBodyConverter为例,返回类型是我们需要的Bitmap,在这个自定义转换器中,将网络响应转换成Bitmap并返回。
class BitmapResponseBodyConverter : Converter<ResponseBody, Bitmap?> {
override fun convert(value: ResponseBody): Bitmap? {
value.use {
return try {
val data: ByteArray = value.bytes()
BitmapFactory.decodeByteArray(data, 0, data.size)
} catch (ignore: Exception) {
null
}
}
}
}后面在业务中拿到的网络请求结果就是可以直接用的Bitmap,而不是ResponseBody。是不是很方便!
2.3、使用自定义Converter
这里演示,定义了一个接口。Retrofit中有很多注解,用来自定义网络请求RESTful接口,详见Retrofit注解详细探究一文。
interface GetBitmapService {
@GET("/upload/{path}")
fun getBitmap(@Path("path") path: String): Call<Bitmap>
}上面的接口返回参数类型是Bitmap,也就是直接从网络请求中获取Bitmap。
接下来构造Retrofit实例,在前面1.2节我们已经知道,Converter.Factory的添加顺序也会影响结果的转换。先添加处理范围小的转换器工厂,最后添加通用转换器工厂兜底。
val retrofit = Retrofit.Builder()
.baseUrl("http://quibbler.cn/")
.addConverterFactory(BitmapConverterFactory.getInstance())
.addConverterFactory(GsonConverterFactory.create())
.build()这里演示直接获取服务器avatar/000/1.png路径中的图片,网络请求结果直接拿到Bitmap,而不用在多进行额外的解析操作。
val call: Call<Bitmap> = bitmapService.getBitmap("avatar/000/1.png")
call.enqueue(object : Callback<Bitmap?> {
override fun onResponse(call: Call<Bitmap?>, response: Response<Bitmap?>) {
//返回响应body()类型就是Bitmap?
val bitmap: Bitmap? = response.body()
imageView.setImageBitmap(bitmap)
}
override fun onFailure(call: Call<Bitmap?>, t: Throwable) {
//TODO
}
})