Retrofit 源码阅读笔记(二)
第一篇文章中介绍了 Retrofit
的动态代理实现、方法注解解析等等内容:Retrofit 源码阅读笔记(一)。
本篇文章是系列文章的第二篇。
方法参数解析
在第一篇文章中介绍了 Http
请求的解析是通过 RequestFactory.Builder#build()
方法完成,在它的内部又分为两步来完成解析,通过 parseMethodAnnotation()
方法完成方法注解的解析(该方法已经在前面的文章中介绍了),通过 parseParameter()
来完成对方法参数的解析,方法参数的解析就是本节的内容。
首先我们来看看 RequestFactory.Builder#parseParameter()
方法的源码:
Java
private @Nullable ParameterHandler<?> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler<?> result = null;
if (annotations != null) {
// 遍历当前参数的注解
for (Annotation annotation : annotations) {
// 解析注解,如果 annotationAction 不为空,就表示当前注解解析成功
ParameterHandler<?> annotationAction =
parseParameterAnnotation(p, parameterType, annotations, annotation);
// 失败就继续查找
if (annotationAction == null) {
continue;
}
// 表示有重复的注解,报错。
if (result != null) {
throw parameterError(
method, p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
}
// 如果没有找到 Retrofit 的注解
if (result == null) {
// 这里表示是最后一个参数。
if (allowContinuation) {
try {
if (Utils.getRawType(parameterType) == Continuation.class) {
// 这里表示是 Kotlin 协程的 suspend 函数
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
// 非 suspend 的 Continuation.class 参数没有注解,直接报错。
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
在请求参数中必须有 Retrofit
的参数注解修饰,而且只能有一个。有一种参数可以例外,那就是 Kotlin
协程中的 suspend
函数的最后一个参数可以不用注解修饰,那个参数固定为 Continuation
类型。你又和 Retrofit
学会了如何判断 suspend
函数😂。
我们继续看看 parseParameterAnnotation()
函数的实现,由于它的实现代码比较长,我就分不同的注解来分析:
@Url
@Url
注解表示该参数是请求的完整地址,他和方法注解中的 Path
会冲突。
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
// 校验类型
validateResolvableType(p, type);
// 如果已经获取到 Url 报错。
if (gotUrl) {
throw parameterError(method, p, "Multiple @Url method annotations found.");
}
// 如果已经有 @Path 注解,报错
if (gotPath) {
throw parameterError(method, p, "@Path parameters may not be used with @Url.");
}
// 如果 @Query 注解在 @Url 注解前报错
if (gotQuery) {
throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
}
// 如果 @QueryName 注解在 @Url 注解前报错
if (gotQueryName) {
throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
}
// 如果 @QueryMap 注解在 @Url 注解前报错
if (gotQueryMap) {
throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
}
// 如果在方法的注解中已经获取到了 Path,报错。
if (relativeUrl != null) {
throw parameterError(method, p, "@Url cannot be used with @%s URL", httpMethod);
}
gotUrl = true;
// 参数类型只支持以下类型
if (type == HttpUrl.class
|| type == String.class
|| type == URI.class
|| (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
return new ParameterHandler.RelativeUrl(method, p);
} else {
// 类型不支持报错
throw parameterError(
method,
p,
"@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
}
}
// ...
}
这里对 @Url
注解做一个总结:
- 不能使用多个
@Url
注解。 - 与
@Path
注解冲突。 @Query
、@QueryName
、@QueryMap
只能在@Url
之后。- 与方法注解中的相对地址冲突。
- 参数的类型只能是
HttpUrl
(OkHttp
中的类)、String
、URI
、Uri
(Android
中的类)其中之一。 - 参数注解处理类是
ParameterHandler.RelativeUrl
。
我们再来看看 ParameterHandler.RelativeUrl
源码:
Java
static final class RelativeUrl extends ParameterHandler<Object> {
private final Method method;
private final int p;
RelativeUrl(Method method, int p) {
this.method = method;
this.p = p;
}
// apply 函数就是处理函数
@Override
void apply(RequestBuilder builder, @Nullable Object value) {
if (value == null) {
throw Utils.parameterError(method, p, "@Url parameter is null.");
}
// 直接把 @Url 的参数设置到 RequestBuilder 中
builder.setRelativeUrl(value);
}
}
构建 RequestBuilder
的处理方法就是 apply()
方法,在 RelativeUrl
中直接就是把参数值通过 RequestBuilder#setRelativeUrl()
方法设置进去。
@Path
@Path
就是前面一篇文章中提到的相对路径占位符。我这里举一个例子:
Java
@POST("/a/{name}/c")
Call<String> foo(@Path("name") String path);
在执行时 Path
中的 {name}
就会被替换成 path
参数中的值。
我们来看看 @Path
注解处理的代码:
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof Path) {
validateResolvableType(p, type);
// 在 @Query 之后报错
if (gotQuery) {
throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
}
// 在 @QueryName 之后报错
if (gotQueryName) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
}
// 在 @QueryMap 之后报错
if (gotQueryMap) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
}
// 与 @Url 冲突
if (gotUrl) {
throw parameterError(method, p, "@Path parameters may not be used with @Url.");
}
// 在方法注解中必须已经设置相对路径
if (relativeUrl == null) {
throw parameterError(
method, p, "@Path can only be used with relative url on @%s", httpMethod);
}
gotPath = true;
Path path = (Path) annotation;
// 获取相对路径占位符的名字
String name = path.value();
// 校验占位符的名字是否存在
validatePathName(p, name);
// 获取当前参数类型转换成 String 类型的转换器。
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());
}
// ...
}
对 @Path
做一个总结:
- 定义必须在
@Query
、@QueryName
、@QueryMap
等注解之前。 - 与
@Url
注解冲突 - 在方法注解中必须定义相对路径
@Path
注解中的名字必须在相对路径的占位符中存在- 获取参数类型转换成
String
类型的Converter
,这个就是一个类型转换器,很多地方都用它做类型转换,我们也可以自定义相关的ConverterFactory
来完成我们自己需要的类型转换,开发中常见的就是Json
转RequestBody
,RequestBody
转Json
,我们后面会单独分析ConverterFactory
。 - 参数处理类是
ParameterHandler.Path
。
看看 ParameterHandler.Path
的源码:
Java
static final class Path<T> extends ParameterHandler<T> {
private final Method method;
private final int p;
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Path(Method method, int p, String name, Converter<T, String> valueConverter, boolean encoded) {
this.method = method;
this.p = p;
this.name = Objects.requireNonNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) {
throw Utils.parameterError(
method, p, "Path parameter \"" + name + "\" value must not be null.");
}
// 直接将原来的占位符,替换成真实的值。
builder.addPathParam(name, valueConverter.convert(value), encoded);
}
}
直接通过 RequestBuilder#addPathParam()
方法将相对路径的参数替换成真实的值,参数的值通过 Converter#convert()
方法转换成 String
。
@Query
@Query
注解就不用多说了,就是 Http
协议中的 Query
参数,这里注意 @Query
的 Key
是可以重复的,如果重复的话就当成是一个数组处理。
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof Query) {
validateResolvableType(p, type);
Query query = (Query) annotation;
// 从注解中获取参数名字
String name = query.value();
boolean encoded = query.encoded();
// 获取参数类型
Class<?> rawParameterType = Utils.getRawType(type);
gotQuery = true;
if (Iterable.class.isAssignableFrom(rawParameterType)) {
// 如果是迭代器类型
if (!(type instanceof ParameterizedType)) {
throw parameterError(
method,
p,
rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
// 获取迭代器泛型类型
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
// 获取转换成 String 的 Converter
Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).iterable();
} else if (rawParameterType.isArray()) {
// 如果是数组,处理方式和迭代器类似
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter<?, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).array();
} else {
// 普通类型,处理方式更加简单。
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Query<>(name, converter, encoded);
}
// ...
}
对 @Query
做一个总结:
@Query
可以处理数组或者迭代器类型的参数,还可以处理普通类型的参数。- 参数最终需要转换成
String
,如果是迭代器或者数组,是他们的Element
需要转换成String
。 - 普通类型的参数处理类是
ParameterHandler.Query
,如果是迭代器和数组会调用它的iterable()
与array()
方法转换。
看看 ParameterHandler.Query
的源码:
Java
static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = Objects.requireNonNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
// 参数为空跳过
if (value == null) return; // Skip null values.
// 转换成 String
String queryValue = valueConverter.convert(value);
// 空跳过
if (queryValue == null) return; // Skip converted but null values
// 将 Query参数添加到 RequestBuilder 中
builder.addQueryParam(name, queryValue, encoded);
}
}
首先将参数通过 Converter
转换成 String
,通过 RequestBuilder#addQueryParam()
方法添加到 Request
中。
@QueryName
它和 @Query
差不多,只是它的 value
值是空的,就只是指定可以空的 Query
。它的处理代码也几乎和 @Query
一样的,只是它的参数处理类换成了 ParameterHandler.QueryName
,我们直接看看它的源代码:
Java
static final class QueryName<T> extends ParameterHandler<T> {
private final Converter<T, String> nameConverter;
private final boolean encoded;
QueryName(Converter<T, String> nameConverter, boolean encoded) {
this.nameConverter = nameConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
// Value 值直接设置为空
builder.addQueryParam(nameConverter.convert(value), null, encoded);
}
}
基本可以说和 Query
一模一样,只是 value
值设置为空。
@QueryMap
相对于 @Query
注解只能指定单个 Query
,而 @QueryMap
就可以指定多个 Query
,它的参数类型必须是 Map
。
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof QueryMap) {
validateResolvableType(p, type);
Class<?> rawParameterType = Utils.getRawType(type);
gotQueryMap = true;
// 参数类型必须是 Map 类型
if (!Map.class.isAssignableFrom(rawParameterType)) {
throw parameterError(method, p, "@QueryMap parameter type must be Map.");
}
Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
if (!(mapType instanceof ParameterizedType)) {
throw parameterError(
method, p, "Map must include generic types (e.g., Map<String, String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
// Map 的 Key 的类型
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
// key 的类型必须是 String
if (String.class != keyType) {
throw parameterError(method, p, "@QueryMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
// 通过 value 类型找到转换成 String 类型的 Converter。
Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);
return new ParameterHandler.QueryMap<>(
method, p, valueConverter, ((QueryMap) annotation).encoded());
}
// ...
}
简单总结一下:
- 参数类型必须是
Map
,Map
的Key
的类型必须是String
。 - 需要将
Map
的Value
转换成String
。 - 最后的参数处理类是
ParameterHandler.QueryMap
。
再来看看 ParameterHandler.QueryMap
的源码:
Java
static final class QueryMap<T> extends ParameterHandler<Map<String, T>> {
private final Method method;
private final int p;
private final Converter<T, String> valueConverter;
private final boolean encoded;
QueryMap(Method method, int p, Converter<T, String> valueConverter, boolean encoded) {
this.method = method;
this.p = p;
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
if (value == null) {
throw Utils.parameterError(method, p, "Query map was null");
}
// 遍历 Map
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
// Key 不能为空
if (entryKey == null) {
throw Utils.parameterError(method, p, "Query map contained null key.");
}
T entryValue = entry.getValue();
if (entryValue == null) {
throw Utils.parameterError(
method, p, "Query map contained null value for key '" + entryKey + "'.");
}
// 将 Value 转换成 String
String convertedEntryValue = valueConverter.convert(entryValue);
// Value 不能为空
if (convertedEntryValue == null) {
throw Utils.parameterError(
method,
p,
"Query map value '"
+ entryValue
+ "' converted to null by "
+ valueConverter.getClass().getName()
+ " for key '"
+ entryKey
+ "'.");
}
// 添加 Query
builder.addQueryParam(entryKey, convertedEntryValue, encoded);
}
}
}
参数的 Key
,Value
都不能为空,其他的就和 @Query
类似了。
@Header
用来描述 Http
请求的 Header
,在源码中的处理方式和 @Query
注解一模一样,就不看它的源码了,看看它的参数处理类 ParameterHandler.Header
源码:
Java
Header(String name, Converter<T, String> valueConverter) {
this.name = Objects.requireNonNull(name, "name == null");
this.valueConverter = valueConverter;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
String headerValue = valueConverter.convert(value);
if (headerValue == null) return; // Skip converted but null values.
builder.addHeader(name, headerValue);
}
}
上面的代码也是朴实无华,直接调用 ReqeustBuilder#addHeader()
方法添加 Http
的 Header
。
@HeaderMap
@HeaderMap
与 @Header
的关系,就相当于 @QueryMap
与 @Query
的关系,它的处理方式和 @QueryMap
一样,源码就不看了,这里看它的参数处理类 ParameterHandler.HeaderMap
的源码实现:
Java
static final class HeaderMap<T> extends ParameterHandler<Map<String, T>> {
private final Method method;
private final int p;
private final Converter<T, String> valueConverter;
HeaderMap(Method method, int p, Converter<T, String> valueConverter) {
this.method = method;
this.p = p;
this.valueConverter = valueConverter;
}
@Override
void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
if (value == null) {
throw Utils.parameterError(method, p, "Header map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String headerName = entry.getKey();
if (headerName == null) {
throw Utils.parameterError(method, p, "Header map contained null key.");
}
T headerValue = entry.getValue();
if (headerValue == null) {
throw Utils.parameterError(
method, p, "Header map contained null value for key '" + headerName + "'.");
}
builder.addHeader(headerName, valueConverter.convert(headerValue));
}
}
}
也没有什么特别之处,相对于 @Header
的处理,@HeaderMap
会遍历它的所有 Key-Value
然后分别调用 RequestBuilder#addHeader()
方法。
@Field
仅限方法被 @FormUrlEncoded
修饰的方法,表示表单类型的 Body
的参数。
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof Field) {
validateResolvableType(p, type);
// 方法没有被 @FormUrlEncoded 报错
if (!isFormEncoded) {
throw parameterError(method, p, "@Field parameters can only be used with form encoding.");
}
Field field = (Field) annotation;
// 获取参数名
String name = field.value();
boolean encoded = field.encoded();
gotField = true;
Class<?> rawParameterType = Utils.getRawType(type);
if (Iterable.class.isAssignableFrom(rawParameterType)) {
// 如果是迭代器类型的参数
if (!(type instanceof ParameterizedType)) {
throw parameterError(
method,
p,
rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
// 获取迭代器泛型的参数类型
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
// 获取迭代器类型转 String 的 Converter。
Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.Field<>(name, converter, encoded).iterable();
} else if (rawParameterType.isArray()) {
// 数组类型处理
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter<?, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Field<>(name, converter, encoded).array();
} else {
// 除数组,迭代器的其他类型
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Field<>(name, converter, encoded);
}
// ...
}
简单总结(和 @Query
也比较类似):
- 方法必须有
@FormUrlEncoded
修饰。 - 处理迭代器类型和数组类型,生成的参数处理器通过
Parameterhandelr.Field
的iterable()
和array()
方法处理后生成的处理器,他们的Converter
也都是迭代器或者数组的泛型类型转String
。 - 除了数组和迭代器的其他的普通类型,直接用当前类型转
String
的Converter
,使用的参数处理器是Parameterhandelr.Field
。
继续看看 Parameterhandelr.Field
的源码实现:
Java
static final class Field<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Field(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = Objects.requireNonNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
String fieldValue = valueConverter.convert(value);
// 跳过空的数据
if (fieldValue == null) return; // Skip null converted values
// 将 Field 添加到参数中
builder.addFormField(name, fieldValue, encoded);
}
}
跳过空的数据,通过 RequestBuilder#addFormField()
将表单 Feild
参数添加到 Request
中。
@FeildMap
@FeildMap
与 @Feild
的关系,就相当于 @QueryMap
与 @Query
的关系,处理逻辑和 @QueryMap
类似(只是有 @FormUrlEncoded
限制),源代码就贴了,它的参数处理类是 ParameterHandler.FieldMap
,看看它的源码:
Java
static final class FieldMap<T> extends ParameterHandler<Map<String, T>> {
private final Method method;
private final int p;
private final Converter<T, String> valueConverter;
private final boolean encoded;
FieldMap(Method method, int p, Converter<T, String> valueConverter, boolean encoded) {
this.method = method;
this.p = p;
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
if (value == null) {
throw Utils.parameterError(method, p, "Field map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
if (entryKey == null) {
throw Utils.parameterError(method, p, "Field map contained null key.");
}
T entryValue = entry.getValue();
if (entryValue == null) {
throw Utils.parameterError(
method, p, "Field map contained null value for key '" + entryKey + "'.");
}
String fieldEntry = valueConverter.convert(entryValue);
if (fieldEntry == null) {
throw Utils.parameterError(
method,
p,
"Field map value '"
+ entryValue
+ "' converted to null by "
+ valueConverter.getClass().getName()
+ " for key '"
+ entryKey
+ "'.");
}
builder.addFormField(entryKey, fieldEntry, encoded);
}
}
}
和 Parameterhandelr.Field
相比就是会遍历每个 Map
中的 Key-Value
,然后分别调用 RequestBuilder#addFormField()
方法添加 Field
参数。
@Part
方法中必须有 @Multipart
修饰的方法才能,使用带 @Part
注解的参数,他表示是 Multipart
类型 RequestBody
中的一部分。
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof Part) {
validateResolvableType(p, type);
// 必须有 @Multipart 修饰
if (!isMultipart) {
throw parameterError(
method, p, "@Part parameters can only be used with multipart encoding.");
}
Part part = (Part) annotation;
gotPart = true;
// 从注解中获取 Part 的名字
String partName = part.value();
Class<?> rawParameterType = Utils.getRawType(type);
// 如果名字为空
if (partName.isEmpty()) {
// 如果类型是迭代器
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(
method,
p,
rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
// 获取迭代器泛型对象的类型
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
// 如果迭代器类型的参数不是 MultipartBody.Part 类型报错
if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
throw parameterError(
method,
p,
"@Part annotation must supply a name or use MultipartBody.Part parameter type.");
}
return ParameterHandler.RawPart.INSTANCE.iterable();
} else if (rawParameterType.isArray()) {
// 数组处理
Class<?> arrayComponentType = rawParameterType.getComponentType();
if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
throw parameterError(
method,
p,
"@Part annotation must supply a name or use MultipartBody.Part parameter type.");
}
return ParameterHandler.RawPart.INSTANCE.array();
} else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
// 其他类型的处理
return ParameterHandler.RawPart.INSTANCE;
} else {
throw parameterError(
method,
p,
"@Part annotation must supply a name or use MultipartBody.Part parameter type.");
}
} else {
// 以下的逻辑表示名字不为空
// 构建一个带 PartName 和 Encoding 的 Header。
Headers headers =
Headers.of(
"Content-Disposition",
"form-data; name=\"" + partName + "\"",
"Content-Transfer-Encoding",
part.encoding());
// 如果参数类型是迭代器
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(
method,
p,
rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
// 获取迭代器泛型的类型
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
// 如果迭代器泛型类型是 MultipartBody.Part,就报错
if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
throw parameterError(
method,
p,
"@Part parameters using the MultipartBody.Part must not "
+ "include a part name in the annotation.");
}
// 获取将迭代器泛型类型转 RequestBody 的 Converter
Converter<?, RequestBody> converter =
retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
return new ParameterHandler.Part<>(method, p, headers, converter).iterable();
} else if (rawParameterType.isArray()) {
// 数组处理
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
throw parameterError(
method,
p,
"@Part parameters using the MultipartBody.Part must not "
+ "include a part name in the annotation.");
}
Converter<?, RequestBody> converter =
retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
return new ParameterHandler.Part<>(method, p, headers, converter).array();
} else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
throw parameterError(
method,
p,
"@Part parameters using the MultipartBody.Part must not "
+ "include a part name in the annotation.");
} else {
// 其他普通类型处理
Converter<?, RequestBody> converter =
retrofit.requestBodyConverter(type, annotations, methodAnnotations);
return new ParameterHandler.Part<>(method, p, headers, converter);
}
}
}
// ...
}
@Part
和 @Query
注解一样都是单独处理数组和迭代器类型的数据的,处理方式也类似,后面的描述也就不分析了,由于 Part
类型是必须要一个 name
,这个 name
可以从 @Part
注解中获取,也可以从参数类型为 MultipartBody.Part
中获取。所以代码就分为了 @Part
中的 name
为空和不为空两种处理逻辑。
@Part
中的 name
为空:
- 参数类型必须是
MultipartBody.Part
。 - 参数处理器是
ParameterHandler.RawPart
,使用的是一个单例对象。
@Part
中的 name
不为空:
- 参数类型不能是
MultipartBody.Part
。 - 将
@Part
中的name
和encoding
参数构建成一个新的Headers
。 - 需要找到一个将参数类型转换成
RequestBody
的Converter
。 - 参数处理器是
ParameterHandler.Part
。
我们先看看 name
不为空的参数处理器 ParameterHandler.RawPart
的源码:
Java
static final class RawPart extends ParameterHandler<MultipartBody.Part> {
static final RawPart INSTANCE = new RawPart();
private RawPart() {}
@Override
void apply(RequestBuilder builder, @Nullable MultipartBody.Part value) {
if (value != null) { // Skip null values.
builder.addPart(value);
}
}
}
直接通过 RequestBuilder#addPart()
方法添加 Part
。
然后再看看 name
为空的参数处理器 ParameterHandelr.Part
的源码:
Java
static final class Part<T> extends ParameterHandler<T> {
private final Method method;
private final int p;
private final okhttp3.Headers headers;
private final Converter<T, RequestBody> converter;
Part(Method method, int p, okhttp3.Headers headers, Converter<T, RequestBody> converter) {
this.method = method;
this.p = p;
this.headers = headers;
this.converter = converter;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) {
if (value == null) return; // Skip null values.
RequestBody body;
try {
// 转换成 RequestBody
body = converter.convert(value);
} catch (IOException e) {
throw Utils.parameterError(method, p, "Unable to convert " + value + " to RequestBody", e);
}
// 添加 part,注意这里传递了一个 PartBody 的 header
builder.addPart(headers, body);
}
}
和 RawPart
中不同的是,首先会通过 Converter
将参数转换成 RequestBody
,然后调用 RequestBuilder#addPart()
方法,注意这里还把之前生成的 Headers
也传递进去了,它会作为 MultipartBody.Part
中的 Headers
。
@PartMap
@PartMap
与 @Part
的关系,就相当于 @QueryMap
与 @Query
的关系,跳过源码分析了,直接看看它的参数处理器的类 ParameterHandler.PartMap
。
Java
static final class PartMap<T> extends ParameterHandler<Map<String, T>> {
private final Method method;
private final int p;
private final Converter<T, RequestBody> valueConverter;
private final String transferEncoding;
PartMap(
Method method, int p, Converter<T, RequestBody> valueConverter, String transferEncoding) {
this.method = method;
this.p = p;
this.valueConverter = valueConverter;
this.transferEncoding = transferEncoding;
}
@Override
void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
if (value == null) {
throw Utils.parameterError(method, p, "Part map was null.");
}
// 遍历 Map
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
// Key 不能为空
if (entryKey == null) {
throw Utils.parameterError(method, p, "Part map contained null key.");
}
T entryValue = entry.getValue();
// Value 不能为空
if (entryValue == null) {
throw Utils.parameterError(
method, p, "Part map contained null value for key '" + entryKey + "'.");
}
// 构建 Part 的 Header。
okhttp3.Headers headers =
okhttp3.Headers.of(
"Content-Disposition",
"form-data; name=\"" + entryKey + "\"",
"Content-Transfer-Encoding",
transferEncoding);
// 添加 part
builder.addPart(headers, valueConverter.convert(entryValue));
}
}
}
和 @Part
相比,也就是添加多个 Part Body
到 RequestBuilder
中。
@Body
描述请求的 Request Body
,源码如下:
Java
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
// ...
if (annotation instanceof Body) {
validateResolvableType(p, type);
// 不能是 Multipart 和 FormUrlEncoded
if (isFormEncoded || isMultipart) {
throw parameterError(
method, p, "@Body parameters cannot be used with form or multi-part encoding.");
}
// 不能重复添加 Body
if (gotBody) {
throw parameterError(method, p, "Multiple @Body method annotations found.");
}
Converter<?, RequestBody> converter;
try {
// 查找 RequestBody 的 Converter.
converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
} catch (RuntimeException e) {
// Wide exception range because factories are user code.
throw parameterError(method, e, p, "Unable to create @Body converter for %s", type);
}
gotBody = true;
return new ParameterHandler.Body<>(method, p, converter);
}
// ...
}
简单描述一下上面的代码:
- 方法不能有
@Multipart
或者@FormUrlEncoded
注解修饰。 - 不能添加多个
Body
。 - 查找
Request Body
的Converter
。 - 参数处理器是
ParameterHandler.Body
。
再看看 ParameterHandler.Body
的源码:
Java
static final class Body<T> extends ParameterHandler<T> {
private final Method method;
private final int p;
private final Converter<T, RequestBody> converter;
Body(Method method, int p, Converter<T, RequestBody> converter) {
this.method = method;
this.p = p;
this.converter = converter;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) {
if (value == null) {
throw Utils.parameterError(method, p, "Body parameter value must not be null.");
}
RequestBody body;
try {
body = converter.convert(value);
} catch (IOException e) {
throw Utils.parameterError(method, e, p, "Unable to convert " + value + " to RequestBody");
}
builder.setBody(body);
}
}
我自己都不想解释了,代码很简单,自己看。。。
最后
终于看完了方法参数解析的代码,很多的代码都是一些模版代码,阅读起来很简单,到这里 RequestFactory
的代码也就分析完了,也就是 Request
的解析,其中包括方法的解析和方法参数的解析。