-
本文概述
Retrofit作为主流的网络框架,采用注解和接口的方式封装请求,使得调用过程变得优雅又简洁,优雅的背后肯定是卓越的设计,所以从源码角度分析一下Retrofit是如何做到的,整理成文。
-
使用回顾
public interface GitHubService { @GET("group/{id}/users?sort=desc") Call
- > groupList(@Path("id") int groupId);
/**
* 和上面的是一样的意义
*
* @param groupId
* @param sort
* @return
*/
@GET("group/{id}/users")
Call
- > groupList(@Path("id") int groupId, @Query("sort") String sort);
/**
* 相当于group/{id}/users?options.key[0]=options.value[0] & options.key[1]=options.value[1] & ...
*
* @param groupId
* @param options
* @return
*/
@GET("group/{id}/users")
Call
- > groupList(@Path("id") int groupId, @QueryMap Map
options); @POST("users/new") Call createUser(@Body User user); @FormUrlEncoded @POST("user/edit") Call updateUser(@Field("first_name") String first, @Field("last_name") String last); @Multipart @PUT("user/photo") Call updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description); /** * 静态Header * @return */ @Headers("Cache-Control: max-age=640000") @GET("widget/list") Call - > widgetList();
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call
getUserS(@Path("username") String username); /** * 动态设置Header * @param authorization * @return */ @GET("user") Call getUser(@Header("Authorization") String authorization); @GET("user") Call getUser(@HeaderMap Map headers); } public static void retrofitDemo(String url) { OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(url) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); GitHubService service = retrofit.create(GitHubService.class); try { List
users = service.groupList(1001).execute().body(); } catch (IOException e) { e.printStackTrace(); } } 可以看到,Retrofit是一种更上层的封装,你可以设置不同的网络框架client,这里是OkHttpClient,设置baseUrl,之后会和interface类中的请求注解路由参数结合在一起构成整体的url,不用像OkHttpClient每次请求都要传入整个url,ConverterFactory可以根据想要解析的格式自由选择,通常我们使用的都是bean->json、json->bean,现在这部分工作我们可以交给Retrofit去做,其他的Converter还有:
- 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
- JAXB:
com.squareup.retrofit2:converter-jaxb
- Scalars (primitives, boxed, and String):
com.squareup.retrofit2:converter-scalars
通过Retrofit实例构造出调用service的实例,调用其方法获取Response数据,简单的调用背后是复杂又巧妙的抽象设计,下面跟着源码好好的学习一下它的原理。
- Gson:
-
源码分析
首先baseUrl就是个验证并将url解析成http或者https格式并保存成HttpUrl对象的方法,client方法将OkHttpClient绑定到Retrofit.Builder,addConverterFactory把第三方的解析库对象添加进一个List
类型的集合,build方法就是构建出Retrofit实例: public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List
callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); // Make a defensive copy of the converters. List converterFactories = new ArrayList<>( 1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters that consume all types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories()); return new Retrofit( callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); } 可以看到,build里面还加入了容错默认处理。baseUrl不能为空,这是必须参数之一,callFactory默认OkHttpClient,这里还有个Executor需要设置,因为我们外面没有设置过这个,所以它是null,然后通过platform的defaultCallbackExecutor方法创建,platform是通过实例话Builder时通过Platform.get()得到的:
private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { return "Dalvik".equals(System.getProperty("java.vm.name")) ? new Android() // : new Platform(true); }
因为我们是android,所以这里是Dalvik虚拟机,因此是Android()作为platform,它的defaultCallbackExecutor如下:
@Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } static final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } }
所以这里使用MainThreadExecutor作为callbackExecutor,可以看到,Android回调操作通常都是和UI的交互,所以回调操作默认是要回到主线程操作的。
再往下需要注意的是converterFactories除了添加我们外面传入的this.converterFactories之外还添加了一个BuiltInConverters和platform.defaultConverterFactories(),注意添加顺序,platform.defaultConverterFactories(),这个方法实现在Platform中:
List extends Converter.Factory> defaultConverterFactories() { return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList(); }
hasJava8Types是在Platfrom构造方法中指定的,这里用的是Android(),所以hasJava8Types默认是false,所以这里defaultConverterFactories返回emptyList(),所以此时converterFactories里面有BuiltInConverters和我们设置的一个Converter(这里是GsonConverterFactory)。
接下来就是调用create获取接口service对象,那interface是怎样可能创建对象呢?还有interface方法的注解是如何映射成最后发送的request的呢?继续往下看:
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. public
T create(final Class service) { //检查service是否符合要求 validateServiceInterface(service); return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class>[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } args = args != null ? args : emptyArgs; return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); } }); } private void validateServiceInterface(Class> service) { //service必须是interface if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } //使用双向队列保存service及其继承的其他interface,add是添加到队尾,然后从头部依次读取判断每个interface是否符合 Deque > check = new ArrayDeque<>(1); check.add(service); while (!check.isEmpty()) { Class> candidate = check.removeFirst(); //这里要求接口不能有泛型 if (candidate.getTypeParameters().length != 0) { StringBuilder message = new StringBuilder("Type parameters are unsupported on ").append(candidate.getName()); if (candidate != service) { message.append(" which is an interface of ").append(service.getName()); } throw new IllegalArgumentException(message.toString()); } Collections.addAll(check, candidate.getInterfaces()); } //创建retrofit对象时传入的最后一个参数,表示是否提前执行loadServiceMethod方法 if (validateEagerly) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { //不能是default方法和静态方法。 //根据isDefaultMethod的javadoc注释,default方法是非静态且有body的,这种方法在interface中存在必须需要default修饰符修饰且需要JDK1.8及以上。 //interface中static修饰的方法需要有方法体 if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { loadServiceMethod(method); } } } } ServiceMethod> loadServiceMethod(Method method) { //之前的validateEagerly如果是true,那在create中调用loadServiceMethod的时候就直接从serviceMethodCache里面取,因为之前已经创建好了 ServiceMethod> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { //创建ServiceMethod的地方 result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result; } 代码的作用我已经在注释中写明了,现在我们知道了实际上retrofit.create创建的是一个GithubService这个接口的动态代理,所以service.groupList实际上调用的动态代理的方法,至于动态代理的对象是怎么创建的,目前只能追溯到native方法,现在先不考虑JVM是怎么实例化代理对象的,我们只需要知道,当我们调用service.groupList的时候会回调到Proxy.newProxyInstance的最后一个参数new InvocationHandler()的invoke方法里,所以最终还是会调用loadServiceMethod方法,前面看到,loadServiceMethod方法创建的result返回对象在于ServiceMethod.parseAnnotations(this, method):
static
ServiceMethod parseAnnotations(Retrofit retrofit, Method method) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //getGenericReturnType()会返回代码中书写的类型,比如说T method(){},则会返回T,而getReturnType会返回Object Type returnType = method.getGenericReturnType(); //验证返回类型是否合法 if (Utils.hasUnresolvableType(returnType)) { throw methodError( method, "Method return type must not include a type variable or wildcard: %s", returnType); } //返回类型不能是void if (returnType == void.class) { throw methodError(method, "Service methods cannot return void."); } return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }
这个方法首先通过RequestFactory.parseAnnotations创建了一个RequestFactory:
```java
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
RequestFactory build() {
//解析作用于方法上的注解(做用于方法上的注解可以有多个,所以是循环解析)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
method,
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError(
method,
"FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
//解析作用于方法参数上的注解(注意parameterAnnotationsArray是一个二维数组,因为有多个参数,每个参数又可能拥有多个注解,但是只允许有一个注解)
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
if (relativeUrl == null && !gotUrl) {
throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError(method, "Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError(method, "Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError(method, "Multipart method must contain at least one @Part.");
}
return new RequestFactory(this);
}
首先调用了parseMethodAnnotation解析方法上的注解:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
阅读代码可知,判断Annotation的类型,调用parseHttpMethodAndPath对应解析,注意只有PATCH、POST、PUT的hasBody参数传入的是true,不同的是Headers注解调用parseHeaders(),Multipart和FormUrlEncoded(表单)是互斥的,它俩分别结合PUT和POST使用,所以没有path。首先看一下parseHttpMethodAndPath方法:
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
//不允许重复赋值
if (this.httpMethod != null) {
throw methodError(
method,
"Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod,
httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
//如果没有path则返回
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
//queryParams是?后面的内容
String queryParams = value.substring(question + 1);
//PARAM_URL_REGEX是\{XXX}\这种样式,不允许出现在?后面,需要使用@Query注解在方法参数里添加
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError(
method,
"URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.",
queryParams);
}
}
//比如/table?id=1,this.relativeUrl就是/table?id=1
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
static Set parsePathParameters(String path) {
Matcher m = PARAM_URL_REGEX.matcher(path);
Set patterns = new LinkedHashSet<>();
while (m.find()) {
patterns.add(m.group(1));
}
return patterns;
}
parsePathParameters会把?前面的\{paramName}\形式的参数名存到一个LinkedHashSet中,为什么是链式的,因为如果方法参数中没有指定参数名注解,则会按照顺序对应赋值。
parseHeaders就是根据“key:value”的形式去解析键值对,Content-Type需要通过MediaType解析:
private Headers parseHeaders(String[] headers) {
Headers.Builder builder = new Headers.Builder();
for (String header : headers) {
int colon = header.indexOf(':');
if (colon == -1 || colon == 0 || colon == header.length() - 1) {
throw methodError(
method, "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
}
String headerName = header.substring(0, colon);
String headerValue = header.substring(colon + 1).trim();
if ("Content-Type".equalsIgnoreCase(headerName)) {
try {
contentType = MediaType.get(headerValue);
} catch (IllegalArgumentException e) {
throw methodError(method, e, "Malformed content type: %s", headerValue);
}
} else {
builder.add(headerName, headerValue);
}
}
return builder.build();
}
回到RequestFactory的build,parseParameter解析方法参数:
private @Nullable ParameterHandler> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler> result = null;
if (annotations != null) {
for (Annotation annotation : annotations) {
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;
}
}
if (result == null) {
if (allowContinuation) {
try {
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
parseParameterAnnotation的源码:
@Nullable
private ParameterHandler> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
validateResolvableType(p, type);
if (gotUrl) {
throw parameterError(method, p, "Multiple @Url method annotations found.");
}
if (gotPath) {
throw parameterError(method, p, "@Path parameters may not be used with @Url.");
}
if (gotQuery) {
throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
}
if (gotQueryName) {
throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
}
if (gotQueryMap) {
throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
}
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.");
}
} else if (annotation instanceof Path) {
validateResolvableType(p, type);
if (gotQuery) {
throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
}
if (gotQueryName) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
}
if (gotQueryMap) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
}
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);
Converter, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());
} else 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()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
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);
}
} else if (annotation instanceof QueryName) {
validateResolvableType(p, type);
QueryName query = (QueryName) annotation;
boolean encoded = query.encoded();
Class> rawParameterType = Utils.getRawType(type);
gotQueryName = true;
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(
method,
p,
rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
Converter, String> converter = retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.QueryName<>(converter, encoded).iterable();
} else if (rawParameterType.isArray()) {
Class> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.QueryName<>(converter, encoded).array();
} else {
Converter, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.QueryName<>(converter, encoded);
}
} else if (annotation instanceof QueryMap) {
validateResolvableType(p, type);
Class> rawParameterType = Utils.getRawType(type);
gotQueryMap = true;
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)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
if (String.class != keyType) {
throw parameterError(method, p, "@QueryMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
Converter, String> valueConverter = retrofit.stringConverter(valueType, annotations);
return new ParameterHandler.QueryMap<>(
method, p, valueConverter, ((QueryMap) annotation).encoded());
} else if (annotation instanceof Header) {
validateResolvableType(p, type);
Header header = (Header) annotation;
String name = header.value();
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()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
Converter, String> converter = retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.Header<>(name, converter).iterable();
} else if (rawParameterType.isArray()) {
Class> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Header<>(name, converter).array();
} else {
Converter, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Header<>(name, converter);
}
} else if (annotation instanceof HeaderMap) {
if (type == Headers.class) {
return new ParameterHandler.Headers(method, p);
}
validateResolvableType(p, type);
Class> rawParameterType = Utils.getRawType(type);
if (!Map.class.isAssignableFrom(rawParameterType)) {
throw parameterError(method, p, "@HeaderMap 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)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
if (String.class != keyType) {
throw parameterError(method, p, "@HeaderMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
Converter, String> valueConverter = retrofit.stringConverter(valueType, annotations);
return new ParameterHandler.HeaderMap<>(method, p, valueConverter);
} else if (annotation instanceof Field) {
validateResolvableType(p, type);
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()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
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);
}
} else if (annotation instanceof FieldMap) {
validateResolvableType(p, type);
if (!isFormEncoded) {
throw parameterError(
method, p, "@FieldMap parameters can only be used with form encoding.");
}
Class> rawParameterType = Utils.getRawType(type);
if (!Map.class.isAssignableFrom(rawParameterType)) {
throw parameterError(method, p, "@FieldMap 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)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
if (String.class != keyType) {
throw parameterError(method, p, "@FieldMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
Converter, String> valueConverter = retrofit.stringConverter(valueType, annotations);
gotField = true;
return new ParameterHandler.FieldMap<>(
method, p, valueConverter, ((FieldMap) annotation).encoded());
} else if (annotation instanceof Part) {
validateResolvableType(p, type);
if (!isMultipart) {
throw parameterError(
method, p, "@Part parameters can only be used with multipart encoding.");
}
Part part = (Part) annotation;
gotPart = true;
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()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
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 {
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()
+ ")");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
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.");
}
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);
}
}
} else if (annotation instanceof PartMap) {
validateResolvableType(p, type);
if (!isMultipart) {
throw parameterError(
method, p, "@PartMap parameters can only be used with multipart encoding.");
}
gotPart = true;
Class> rawParameterType = Utils.getRawType(type);
if (!Map.class.isAssignableFrom(rawParameterType)) {
throw parameterError(method, p, "@PartMap 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)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
if (String.class != keyType) {
throw parameterError(method, p, "@PartMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) {
throw parameterError(
method,
p,
"@PartMap values cannot be MultipartBody.Part. "
+ "Use @Part List or a different value type instead.");
}
Converter, RequestBody> valueConverter =
retrofit.requestBodyConverter(valueType, annotations, methodAnnotations);
PartMap partMap = (PartMap) annotation;
return new ParameterHandler.PartMap<>(method, p, valueConverter, partMap.encoding());
} else if (annotation instanceof Body) {
validateResolvableType(p, type);
if (isFormEncoded || isMultipart) {
throw parameterError(
method, p, "@Body parameters cannot be used with form or multi-part encoding.");
}
if (gotBody) {
throw parameterError(method, p, "Multiple @Body method annotations found.");
}
Converter, RequestBody> converter;
try {
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);
} else if (annotation instanceof Tag) {
validateResolvableType(p, type);
Class> tagType = Utils.getRawType(type);
for (int i = p - 1; i >= 0; i--) {
ParameterHandler> otherHandler = parameterHandlers[i];
if (otherHandler instanceof ParameterHandler.Tag
&& ((ParameterHandler.Tag) otherHandler).cls.equals(tagType)) {
throw parameterError(
method,
p,
"@Tag type "
+ tagType.getName()
+ " is duplicate of parameter #"
+ (i + 1)
+ " and would always overwrite its value.");
}
}
return new ParameterHandler.Tag<>(tagType);
}
return null; // Not a Retrofit annotation.
}
注意retrofit.requestBodyConverter是这里使用GsonConverter把bean转化成json的地方,怎么转换就交给GsonConverter了。至于Retrofit是在哪里用上的Converter转换请求参数的我们放在最后,现在还没走完请求流程不好理解。
类似于解析方法注解一样,针对Url、Path、Query、QueryName、QueryMap、Header、HeaderMap、Field、FieldMap、Part、PartMap、Body、Tag这些注解进行解析。
回到ServiceMethod的parseAnnotations方法,接下来会验证返回类型并最终会返回一个ServiceMethod,看一下Utils.hasUnresolvableType(returnType)方法:
static boolean hasUnresolvableType(@Nullable Type type) {
if (type instanceof Class>) {
return false;
}
//ParameterizedType,比如ArrayList
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
//getActualTypeArguments()得到一个包含String的Type数组,递归调用会判断为Class类型
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
if (hasUnresolvableType(typeArgument)) {
return true;
}
}
return false;
}
//GenericArrayType,比如T[]或T[][],
if (type instanceof GenericArrayType) {
//getGenericComponentType()会得到T和T[](脱一层数组)
return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
}
//TypeVariable,比如T、E这种表示某种类型的
if (type instanceof TypeVariable) {
return true;
}
//WildcardType,? extends String这种通配符表示的,比如ArrayList extends String>这种ParameterType类型的调用parameterizedType.getActualTypeArguments()得到? extends String这种WildcardType类型
if (type instanceof WildcardType) {
return true;
}
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException(
"Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <"
+ type
+ "> is of type "
+ className);
}
返回类型验证没问题了就会通过parseAnnotations创建一个ServiceMethod,看看HttpServiceMethod.parseAnnotations做了什么:
static HttpServiceMethod parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
Converter responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter>) callAdapter,
continuationBodyNullable);
}
}
通过createCallAdapter得到callAdapter:
private static CallAdapter createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
public CallAdapter, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter, ?> nextCallAdapter(
@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
Objects.requireNonNull(returnType, "returnType == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
StringBuilder builder =
new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
可以看到是从callAdapterFactories里面取的,还记得Retrofit.Builder的build的时候吗:
// Make a defensive copy of the adapters and add the default Call adapter.
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
如果我们通过addCallAdapterFactory设置过CallAdapter则至少会有一个DefaultCallAdapterFactory,按照没设置的情况,callAdapterFactories.get(i).get(returnType, annotations, this)就会是:
@Override
public @Nullable CallAdapter, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
//必须是ParameterizedType类型
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call or Call extends Foo>");
}
//getParameterUpperBound内部会通过getActualTypeArguments获取泛型类的类型,这里即是List
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter
同理,converter也一样会走到:
public Converter nextResponseBodyConverter(
@Nullable 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 converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter) converter;
}
}
先看构造Retrofit时第一个被添加的BuiltInConverters的responseBodyConverter:
@Override
public @Nullable Converter 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;
}
if (checkForKotlinUnit) {
try {
if (type == Unit.class) {
return UnitResponseBodyConverter.INSTANCE;
}
} catch (NoClassDefFoundError ignored) {
checkForKotlinUnit = false;
}
}
return null;
}
可见这是泛型类没有明确指定时的容错处理。
我们这里addConverterFactory时传入了GsonConverterFactory.create(),所以去看看GsonConverterFactory的responseBodyConverter:
@Override
public Converter responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
回到HttpServiceMethod的parseAnnotations,最终会返回一个CallAdapted(暂时不考虑kotlin协程的情况)。
然后就是调用动态代理中调用invoke方法,实际上就是HttpServiceMethod中的invoke方法:
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
也就是CallAdapted中的adapt方法:
@Override
protected ReturnT adapt(Call call, Object[] args) {
return callAdapter.adapt(call);
}
所以也就是DefaultCallAdapterFactory的get方法返回的CallAdapter的adapt:
@Override
public Call adapt(Call call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
到现在相当于service.groupList(1001)已经执行完了,接下来就是execute(),也就是这个ExecutorCallbackCall的execute方法:
@Override
public Response execute() throws IOException {
return delegate.execute();
}
delegate就是上面的OkHttpCall,它的execute是:
@Override
public Response execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
getRawCall方法最终调用方法是:
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
这个callFactory经查找就是我们Retrofit.Builder().client()传入的OkHttpClient对象,所以这里的call.execute()就是OkHttpClient的newCall()的execute,再往下就是OkHttpClient的源码了。
至此,Retrofit的网络请求API调用过程就结束了。
那么response是怎么通过Converter解析的呢?还有个parseResponse方法:
Response parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
responseConverter.convert(catchingBody)就是答案,responseConverter是前面的responseBodyConverter返回的GsonResponseBodyConverter:
@Override
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
下面就是Gson框架的部分了。
前面还有一点没讲就是Retrofit怎么引用这个converter解析request参数(即方法参数)的呢?
返回的ParameterHandler的子类会被保存到RequestFactory的parameterHandlers中,后面OkHttpCall调用createRawCall的时候有一句callFactory.newCall(requestFactory.create(args)),这个requestFactory.create中有一句handlers[p].apply(requestBuilder, args[p]),handlers就是parameterHandlers,所以这个apply就是ParameterHandler的相关子类中的apply方法,拿ParameterHandler.Body来说:
@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);
}
converter.convert就调用到了前面设置的GsonRequestBodyConverter。
-
总结
通过源码深刻理解了从调用到解析,Retrofit是如何优雅的通过注解和动态代理的方式把OkHttpClient的调用封装起来,从而使得彼此之间地耦合度大大地降低地。