kotlin 协程学习笔记(二)Retrofit的协程原理
源码解读基于2.9.0
本笔记记录本人阅读源码思路,并非源码解读
不想看阅读源码思路的直接拉倒最下面看伪代码
public <T> T create(final Class<T> 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);
}
});
}
该代码块分为两部分,validateServiceInterface,2Proxy.newProxyInstance
先看 validateServiceInterface 干了什么 通过 level记录方法调用层级
validateServiceInterface level1
private void validateServiceInterface(Class<?> service) {
//判断是否是接口,否则抛出异常
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
//不知道干啥的,百度后是双队列,头尾均可出队列,压队列 队列个数为1 为了Collections.addAll的入参
Deque<Class<?>> check = new ArrayDeque<>(1);
//压入
check.add(service);
//判断不为空
while (!check.isEmpty()) {
Class<?> candidate = check.removeFirst();
//判断参数>0
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里
Collections.addAll(check, candidate.getInterfaces());
}
//上面都是判断逻辑 Collections存了东西
//暂时不知道这是个啥,留着
if (validateEagerly) {
//不知道这又是啥,留着
Platform platform = Platform.get();
//遍历传入的service,
for (Method method : service.getDeclaredMethods()) {
//判断了一坨,
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
//不知道干了啥
loadServiceMethod(method);
}
}
}
}
看完代码以后,遗留几个问题,Collections干了啥,validateEagerly是啥,Platform是啥,loadServiceMethod干了啥, Collections和Platform是其他类,大体看下这两个是干啥的
然后就尴尬的发现,这些方法都是API方法,恶补。。
百度一大坨最后得出代码结论
Collections.addAll(check, candidate.getInterfaces()); 将传入的service中放方法全部插入集合,结合while (!check.isEmpty())可以遍历所有方法
platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())是判断是否是系统或者api方法,所以不是的话必定会执行loadServiceMethod
validateEagerly是本类变量loadServiceMethod是本类方法,再看这两个
一个一个解决,
validateServiceInterface level1-> validateEagerly level2
本类搜代码,搜到了一坨变量赋值,可能有用的是这个方法,翻译下
/**
* When calling {@link #create} on the resulting {@link Retrofit} instance, eagerly validate the
* configuration of all methods in the supplied interface.
*/
//机翻 在生成的改造实例上调用create时,急切地验证所提供接口中所有方法的配置。
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
意思貌似是验证方法配置,没啥用,估计默认都验证
最后看 loadServiceMethod 这里明显返回了一个类型,而且这个方法还有一个地方调用,在 Proxy.newProxyInstance的invoke中,先看代码
validateServiceInterface level1-> loadServiceMethod level2
ServiceMethod<?> loadServiceMethod(Method method) {
//缓存里有就直接返回
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
//加锁判断缓存里有没有
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//干了一坨事,扔到缓存里
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
需要追下ServiceMethod.parseAnnotations 不知道往缓存里塞了啥,但是可以知道在validateServiceInterface中执行的目的就是将ServiceMethod.parseAnnotations的方法扔到缓存里,提供给 Proxy.newProxyInstance的invoke使用,所以下面直接快进到Proxy.newProxyInstance中的loadServiceMethod,继续追下去
Proxy.newProxyInstance level1-> loadServiceMethod level2->ServiceMethod.parseAnnotations level3
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//这是啥?
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//这又是啥?貌似拿了方法的返回类型
Type returnType = method.getGenericReturnType();
//返回api未知类型,抛异常
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
method,
"Method return type must not include a type variable or wildcard: %s",
returnType);
}
//没有返回类型抛异常
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
//又返回了啥?
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
ServiceMethod.parseAnnotations level3 有两个貌似看起来不简单的类,RequestFactory ,HttpServiceMethod深入追下
ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4
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) {
//猜测是处理get那一坨注解
parseMethodAnnotation(annotation);
}
此处省略一坨抛出异常的判断
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);
}
此处省略一坨抛出异常的判断
return new RequestFactory(this);
}
关键方法就三个,parseMethodAnnotation,parameterHandlers,new RequestFactory
ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4->parseMethodAnnotation level 5
private void parseMethodAnnotation(Annotation annotation) {
//一堆ifelse处理标注的注解,并拿到值
//请求方式的注解都执行parseHttpMethodAndPath,并把注解里的值传入
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注解里的
// String method();
/**
* A relative or absolute path, or full URL of the endpoint. This value is optional if the first
* parameter of the method is annotated with {@link Url @Url}.
*
* <p>See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
* this is resolved against a base URL to create the full endpoint URL.
*/
// String path() default "";
// boolean hasBody() default false;
//三个参数传入
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
//header处理
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;
}
}
这里将各种请求类型的值赋值给全局变量
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
//将代码判断省略后的代码
this.httpMethod = httpMethod;
this.hasBody = hasBody;
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
所以parseMethodAnnotation这个代码就是将写的接口上放的所有注解转换成Builder里的成员变量,
RequestFactory.parseAnnotations得到Builder就能拿到 请求方式,请求url等数据
回头继续看
//结合上下代码查看,可以看到这个是
// Builder(Retrofit retrofit, Method method)里的
// this.parameterAnnotationsArray = method.getParameterAnnotations();方法赋值
//method.getParameterAnnotations是获取方法参数前的注解
//所以这里是获取参数所有注解
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);
}
因为代码太多,大体和方法注解一样,这里拿到参数注解去判断是否是Field ,HeaderMap等注解,然后返回一个ParameterHandler.Header而ParameterHandler最终还是向HttpUrl.Builder urlBuilder里塞了入参,这个就不纠结了,毕竟只是为了找协程。。。
但是!!!
在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);
//省略判断代码
result = annotationAction;
}
}
if (result == null) {
if (allowContinuation) {
try {
//判断参数类型是否是Continuation.class 设置一个isKotlinSuspendFunction
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
isKotlinSuspendFunction 从名字上来看,应该是判断suspend关键字方法 suspend和Continuation.class什么关系???
经过一番搜索,终于得知,suspend在编译成字节码后 会变成一个Continuation.class的入参,验证方法很简单,用as提供的tools->kotlin->showKotlinByteCode 查看简单的一个例子
@GET("/1211-1")
suspend fun getEnglishWordsByLaunch(
@Query("count") count: Int?,
): BaseKey<DataKey<EnglishKey>>
字节码文件
public abstract getEnglishWordsByLaunch(Ljava/lang/Integer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@Lretrofit2/http/GET;(value="/1211-1")
@Lorg/jetbrains/annotations/Nullable;() // invisible
// annotable parameter count: 2 (visible)
@Lretrofit2/http/Query;(value="count") // parameter 0
// annotable parameter count: 2 (invisible)
@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
LOCALVARIABLE this Lcom/demo/kotlinapp/net/Urls; L0 L1 0
LOCALVARIABLE count Ljava/lang/Integer; L0 L1 1
所以经过上面的代码分析回到build方法中
RequestFactory build() {
//省略处理方法体的注解
//省略处理方法体判断
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
//处理所有入参标注,如果最后一个是参数Continuation.class类型,将builder中的isKotlinSuspendFunction设置成true
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
//省略其他判断
return new RequestFactory(this);
}
再回到 parseAnnotations
ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//包含各种各样的参数,URL,请求方式,请求体,isKotlinSuspendFunction
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//省略一坨判断
//这里一定处理了什么
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
ServiceMethod.parseAnnotations level3->HttpServiceMethod.parseAnnotations level4
这一大坨代码,看着头疼 一点点啃吧
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//是否是协程方法
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
//是否需要返回Response类型??
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]);
//如果返回类型是Response.class 就是等待返回结果,并且拿到responseType
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
//其他类型需要自己判断是否为空。。。。
}
//通过responseType创建了一个带泛型的Call (相当于返回了一个Call<T>)
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
//这里点进去看了看就是会给注解加一个SkipCallbackExecutor 类型的注解,主要是为了不回掉callback方法
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
//其他的和以前差不多
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
//省略一坨判断异常
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//开始创建call了
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
//不是kotlin方法创建普通CallAdapted
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
//协程返回Response的CallAdapted
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
//协程返回Bod的CallAdapted
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
看来关键方法是在SuspendForResponse和SuspendForBody了,而且Proxy.newProxyInstance level中也执行了这两个返回对象的invoke方法,所以先从SuspendForResponse的invoke入手
Proxy.newProxyInstance level1-> loadServiceMethod level2-> invoke level3
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
而且 SuspendForResponse 中没有重写 invoke方法 所以关键逻辑在SuspendForResponse的adapt中
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
// See SuspendForBody for explanation about this try/catch.
try {
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
可以看到关键代码在awaitResponse中
suspend fun <T> Call<T>.awaitResponse(): Response<T> {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
continuation.resume(response)
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
看到enqueue和suspendCancellableCoroutine恍然大悟
suspendCancellableCoroutine 可以将回调参数转换为协程 用法就是 用resume返回结果
enqueue也清晰了,是call的扩展函数,直接执行的call里的enqueue
java和kotlin的参数不一样是因为编译造成的,kotlin的方法编译后会生成类似以下代码
public static awaitResponse(Lretrofit2/Call;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
所以相当于传入的call执行了enqueue方法,并通过suspendCancellableCoroutine方法转化为协程方法回执到结果中
好的,到此这整个流程就清晰了
最终执行顺序伪代码如下
->Retrofit.Builder()...build().create(你写的请求接口类) //开始准备执行请求
->validateServiceInterface{//做接口类参数验证
->loadServiceMethod(){//通过缓存获取 ServiceMethod
if(缓存中没有该方法的ServiceMethod){
->ServiceMethod.parseAnnotations{
//获取ServiceMethod
->RequestFactory.parseAnnotations(){
//获取是否是kotlin方法isKotlinSuspendFunction
//(suspend 关键字会编译成Continuation,通过最后一个入参判断)
//获取方法上的注解和入参注解,并做验证
}
->HttpServiceMethod.parseAnnotations(){
//通过 isKotlinSuspendFunction 创建 CallAdapted或者SuspendForResponse/SuspendForBody 并返回
}
}
}else{
//直接返回 缓存中ServiceMethod ServiceMethod =CallAdapted或者SuspendForResponse/SuspendForBody
}
}
}
->注册你写的请求接口类的动态代理
->等待你执行方法
->你执行某个接口请求method
->触发动态代理 invoke
-> loadServiceMethod获取到ServiceMethod(CallAdapted/SuspendForResponse/SuspendForBody)
->ServiceMethod.invoke
->new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
->adapt{
if CallAdapted 返回 CallAdapter<ResponseT, ReturnT> 需要手动调用enqueue
if SuspendForResponse {
->KotlinExtensions.awaitResponse(){
通过java入参call 转化成改参数的扩展函数,直接执行enqueue
通过suspendCancellableCoroutine转化协程方法 直接返回Callback接口结果
}
}
if SuspendForBody {
->KotlinExtensions.awaitNullable/await(){
通过java入参call 转化成改参数的扩展函数,直接执行enqueue
通过suspendCancellableCoroutine转化协程方法 直接返回Callback接口结果
}
}
}
->得到invoke结果,若接口为suspend修饰,直接返回SuspendForResponse或SuspendForBody返回的接口结果,否则返回ExecutorCallbackCall接口(详见DefaultCallAdapterFactory)
以上就是通过源码理解Retrofit的协程原理