前言
相信很多小伙伴在使用了Kotlin+协程之后,小伙伴们都是一副这个样子! Retrofit对于协程的支持,是在2.6.0版本之后才开始支持的,在此之前对于在Retrofit中使用协程,主要靠的是Jake Wharton写的开源库retrofit2-kotlin-coroutines-adapter 来支持协程。
本篇的目的不是为了和大家详细讨论Retrofit源码,而是抓住其中的协程相关部分,同时与J神的retrofit2-kotlin-coroutines-adapter 进行一点比较(虽然两个东西都是他写的),来让大家了解,Retrofit是如何实现对协程的支持的。
如果大家对与协程不是太了解,推荐凯哥的码上开学免费课程,当然,最好的是通过官方文档进行学习。
Retrofit的基本思路
Retrofit是一个最典型的动态代理实现对象,通过接口的方法,来为我们生成真正可以进行网络请求的一个对象,然后我们再通过这个方法,获取返回值,完成我们的网络请求。
此处为动态代理的逻辑,也是Retrofit的实现入口:
//这个方法就是我们如何通过动态代理,将一个接口类,转换为一个可以进行网络请求的类
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 (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);
}
});
}
请大家记住这个loadServiceMethod(method).invoke(args)方法
CallAdapter<T, R>转换器
因为我们知道,Retrofit其实是对OkHttp的封装,从这个角度分析,我们需要了解的是,Retrofit是如何将Okhttp的Call 转换为Kotlin的协程的挂起函数
//默认CallAdapter添加逻辑Retrofit.Builder().build()中实现
public Retrofit build() {
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
}
//这个是Platform中的方法
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
//
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
在获取到Call之后,就进入处理的流程了,如果不使用@SkipCallbackExecutor注解,默认会调用会返回ExecutorCallbackCall,也就是后台线程执行能力的一个Call,这个Call大家比较熟悉了。
HttpServiceMethod<ResponseT, ReturnT>
通过跟随动态代理的逻辑,代码会点击到HttpServiceMethod<ResponseT, ReturnT>,这个类的作用就是,怎么返回你在接口类中声明的返回值
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
、、、、、、、、省略一些代码
//注解1
CallAdapter<ResponseT, ReturnT> 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<String>)");
}
// 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<ResponseBody, ResponseT> 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.
//注解2
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
//注解3
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
这里有几个关键的参数,isKotlinSuspendFunction区分是否为挂起函数,continuationWantsResponse是否返回体是Response 注解1,也就是我们上面讲到的Adapter,作用就是返回Call,细节不用太深究,主要讲讲HttpServiceMethod的子类SuspendForBody,也就是真正将响应体转换为挂起函数的过程。
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
此处的invoke函数,也就是我们在代理的实现中,去调用的方法,调用adapt的方法,并传入我们的通过通过解析接口方法,获取到的Call,再调用SuspendForBody.adapt(Call call, Object[] args)
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
private final boolean isNullable;
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
try {
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
再通过Kotlin扩展函数
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException("Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null")
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
最终返回一个挂起函数,并通过continuation,对函数进行挂起、恢复、抛出异常和作用域关闭时协程的中断管理。
结语
可能用很大一部分的开发者,已经用上协程+Jetpack+MVVM这一套的东西了。自己最近也在新项目上尝试,使用新的一个技术栈。网上的相关博客,也查阅了很多,但总是不和我意,也可能因为我没达到别人的境界吧。就是希望大家可以在懂原理的基础上,去使用一些框架。 然后对于文中有不足或者错误的,请大家帮忙指正。 本文纯粹个人的拙见,望各位同好们多多包涵