Retrofit源码解析(二):源码解析
前言
之前我们已经复习了Retrofit的基础用法,Retrofit的源码理解并不复杂,他实现的主要功能就是把接口文件通过注解转化成Okhttp请求,所以我们弄懂了主线,整个Retrofit我们就明白了。
正文
首先复习一下Retrofit的用法:
val retrofit = Retrofit.Builder()
// 必填项
.baseUrl("http://www.baidu.com")
.client(OkHttpClient())
// 对得到的结果进行转换,常用的有加密解密,json转换等等
.addConverterFactory(StringConvertFactory())
// 对返回的结果进行封装,常用的有之间转化成Rxjava对象
// 这里我们简单的进行包装
.addCallAdapterFactory(ResponseWrapperCallAdapterFactory())
.build()
api = retrofit.create(TestApi::class.java)
Retrofit的通过构建者模式创建,这里的重点是:
api = retrofit.create(TestApi::class.java)
我们看看这个create方法是怎么解析接口文件的:
public <T> T create(final Class<T> service) {
// 检查创建的网络请求的服务接口是否可用
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 当调用定义的接口方法的时候,通过动态代理调用自己的逻辑,发起网络请求
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
// 如果参数的类型是Object,直接反射调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 如果是default方法,根据平台类型直接调用
// android平台固定返回false
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 通过Method,封装成okhttp请求
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 发起网络请求
return serviceMethod.adapt(okHttpCall);
}
});
}
通过create方法,创建了指定接口文件的动态代理,每次我们通过动态代理访问对应的方法的时候,都会进入到invoke方法,通过invoke方法,我们可以获得这个方法的注解信息,然后转换成okhttp请求。
首先检查了接口文件是否合法,判断是否是interface接口并且方法书大于0。这里我们还看到了构建Retrofit时设置的validateEagerly属性,如果是true,会在创建接口代理时,解析所有的方法,所以我们一般不会开启这个设置。eagerlyValidateMethods方法内部调用的是loadServiceMethod方法,loadServiceMethod方法解析调用方法的注解信息,封装成ServiceMethod对象。
ServiceMethod<?, ?> loadServiceMethod(Method method) {
// 如果缓存中已经有了这个方法,直接返回
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
// 同步锁
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 把这个方法封装成ServiceMethod对象,保存起来
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
首先检查缓存,如果缓存了这个方法的请求信息,直接返回,否则会重新解析一次,解析的步骤被封装到ServiceMethod.Builder中,所以我们看一下ServiceMethod.Builder的build方法。
public ServiceMethod build() {
// 创建callAdapter
callAdapter = createCallAdapter();
// 得到返回值的类型
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// 创建responseConverter
responseConverter = createResponseConverter();
// 解析注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// 检查是否设置了请求方式
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
// 如果没有添加body,要检查请求的方式是否支持无body请求
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
// 遍历参数的注解
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
// 对注解的参数类型进行检查
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
// 每一个参数都要有注解
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
// 解析参数的注解
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
在build方法中,开始了对注解的解析,这个还做了很多的判断,例如是否设置了url的检查,某些注解使用冲突的问题等等。这个方法中主要看parseMethodAnnotation方法和parseParameter方法。
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);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} 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("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
这个分别解析了方法的注解,我们看到了熟悉@GET、@POST等注解,具体的实现在parseHttpMethodAndPath中:
/**
* 解析方法中注解,保存对应的value
* */
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
// 判断url链接之后的参数是否合法
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
// 保存相对地址
this.relativeUrl = value;
// 从地址中分离出设置的参数
this.relativeUrlParamNames = parsePathParameters(value);
}
parseHttpMethodAndPath先获取注解中网络请求的相对路径,如果设置了相对地址,最后会和baseUrl拼接成完成的请求地址,另外还检查了在相对地址中,是不允许设置参数的,考虑到有些对url链接的地址组成不了解朋友,我们举一个简单的例子:
如果想要在网址中添加参数,需要在末尾加上?,然后通过 key=value&key=value的方式拼接
具体的url了解的格式大家可以自己查看相关资料。看完了解析方法上的直接,接着看解析方法中参数的注解,因为代码实在是太多了,我们就挑其中一个熟悉的注解作为理解的例子:
else if (annotation instanceof Field) {
if (!isFormEncoded) {
throw parameterError(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);
// 如果是集合,需要指定泛型是String
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(p, rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
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);
}
}
我们看到@Field注解必须和@FormUrlEncoded一起使用,把对应的注解的类型的key和设置的参数值加入到表单中去。
现在方法的注解和参数的注解都已经得到了,Retrofit把ServiceMethod和指定的参数封装成OKhttpCall:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
OkhttpCall是ServiceMethod和okhttp请求转换的处理类,他主要是负责成request和处理response,这里贴出几个重要的方法:
生成okhttp的网络请求:
/**
* OKhttpCall.toCall实际上调用的ServiceMethod.toCall方法
* 生成okhttp网络请求
*/
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
// 封装网络请求的信息
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
//
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
// 判断参数的数量是否和方法的数量相等
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
// 把参数添加到requestBuilder中
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
// 调用okhttpClient创建Call对象
return callFactory.newCall(requestBuilder.build());
}
获取okhttp的request信息:
@Override public synchronized Request request() {
// 如果已经有okhttp的call请求,返回call的request
okhttp3.Call call = rawCall;
if (call != null) {
return call.request();
}
// 判断是否创建请求失败过
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw new RuntimeException("Unable to create request.", creationFailure);
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
try {
// 创建okhttp请求
return (rawCall = createRawCall()).request();
} catch (RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
} catch (IOException e) {
creationFailure = e;
throw new RuntimeException("Unable to create request.", e);
}
}
解析okhttp的请求结果:
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
其中在解析网络请求结果的过程中,调用了我们设置的ConvertFactory,对得到的响应结果进行转换:
/** Builds a method return value from an HTTP response body. */
/**
* 对得到的网络请求结果进行转换
* */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
到此为止Retrofit的发起网络请求的整个流程就结束了。
哎?是不是忘了点什么?没错那就是CallAdapter,其实CallAdapter只是对网络请求结果的包装,也就对ConvertFactory的转换结果的封装,回顾一下我们自定义的CallAdapterFactory:
/**
* Created by li.zhipeng on 2018/8/29.
*
* 把得到网络请求结果String,转换成ResponseWrapper
*/
public class ResponseWrapperCallAdapterFactory extends CallAdapter.Factory {
@Override
public CallAdapter<String, ResponseWrapper> get(@NonNull final Type returnType, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) {
return new CallAdapter<String, ResponseWrapper>() {
@Override
public Type responseType() {
return returnType;
}
@Override
public ResponseWrapper adapt(@NonNull Call<String> call) {
try {
return new ResponseWrapper(call.execute().body());
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseWrapper("error");
}
};
}
}
这个类的重点就是:
@Override
public ResponseWrapper adapt(@NonNull Call<String> call) {
try {
return new ResponseWrapper(call.execute().body());
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseWrapper("error");
}
我们在CallAdapter中开启了网络请求,并且把得到的结果封装成我们需要的对象。在动态代理中
serviceMethod.adapt(okHttpCall);
T adapt(Call<R> call) {
return callAdapter.adapt(call);
}
通过ServiceMethod间接调用了我们设置的CallAdapter的adapt方法。
总结
通过我们的对Retrofit的源码的分析,主要需要理解两个类:
ServiceMethod:负责解析方法的注解和方法参数的注解。
OKhttpCall:负责将ServiceMethod和Okhttp的网络请求进行转换,并解析网络请求的结果。
Retrofit的实现的核心就是动态代理,不得不说动态代理在这种场景发挥出了不可思议的作用。
Retrofit的源码分析就到此结束了,有什么不懂的问题欢迎大家留言讨论。