Retrofit 源码阅读笔记(三)
第一篇文章中介绍了 Retrofit 的动态代理实现、方法注解解析等等内容:Retrofit 源码阅读笔记(一)。
第二篇文章中介绍了 Retrofit 是如何解析方法中的参数的信息:Retrofit 源码阅读笔记(二)。
本篇文章是系列文章中的第三篇,介绍 Retrofit 如何构建 Http 请求任务,我个人认为这部分代码是 Retrofit 代码中最复杂的一部分,也是最最核心的一部分,整理好心情准备上车。
Http 请求任务构建
先回顾一下之前的内容,动态代理的方法中都是通过调用 ServiceMethod#invoke() 方法来实现调用的,如果没有缓存的 ServiceMethod 对象,需要通过 ServiceMethod#parseAnnotations() 通过解析方法中的各种注解和方法中的各种参数类型返回值类型构建一个 ServiceMethod 对象,然后供代理方法调用。在 ServiceMethod#parseAnnotations() 方法中,会通过 RequestFactory#parseAnnotations() 方法解析生成一个 RequestFactory 对象,在它里面封装了构建 Http Request 的方法(前面的文章已经分析过了);然后通过 HttpServiceMethod.parseAnnotations() 方法解析生成一个 Http 请求的任务,也就是会返回动态代理中使用的 ServiceMethod 对象,这也是我们今天的主要内容。
在开始之前介绍一下 Kotlin 协程的 suspend 方法,以免后面的源码阅读困难。
假如我定义了这样一个 Kotiln 方法:
Kotlin
suspend fun helloWorld(): String
在经过 Kotlin 编译器处理后,在运行时上面的方法定义变成了如下:
Kotlin
fun helloWorld(continuation: Continuation<String>): Any
方法的返回参数变成了 Object 对象,原来的函数多了一个 Continuation 的参数,它里面的泛型类型就是原来的函数的返回值类型。所以 Retrofit 判断 Kotlin 的 suspend 方法也是通过判断最后一个参数是否是 Continuation,前面的文章中有讲。
从 HttpServiceMethod#parseAnnotations() 实现开始分析:
Java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 是否是 Kotlin 协程 suspend 方法
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
// suspend 方法返回值类型是否是 Response
boolean continuationWantsResponse = false;
// suspend 方法返回 Response Body 是否可以为空
boolean continuationBodyNullable = false;
// 获取方法中的注解
Annotation[] annotations = method.getAnnotations();
Type adapterType;
// 如果是 Kotlin suspend 函数
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
// 获取 Continuation 参数中的泛型的类型,也就是最后的返回值类型
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
// 如果返回值的类型是 Retrofit 中的 Response
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// 获取 Response<T> 中的泛型类型,也就是 Response Body 需要转换的类型
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
// 标记想要 Response
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
}
// suspend 方法的 adatperType 统一为 Call
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
// 添加 @SkipCallbackExecutor 注解表明后续 CallBack 回调线程在主线程(后面我们会看到这部分代码)
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
// 非 suspend 的方法
// 获取 adapter 方法
adapterType = method.getGenericReturnType();
}
// 获取 adapterType 对应的 CallAdapter,也就是通过 CallAdapter 获取对应 adapterType 的实例
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
// 通过 CallAdapter 获取 ResponseBody 转换后的类型
Type responseType = callAdapter.responseType();
// Response Type不允许是 OkHttp 中的 Response
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// Response Type 不允许是 Retrofit 中的 Response
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
// 如果是 HEAD 请求,Response Type 只能是 Void
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// 获取 ResponseBody 到 Response Type 的 Converter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
// 这个 okhttp3.Call.Factory 其实就是 OkhttpClient
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
// 非 suspend 函数
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
// 返回值为 Response 的 suspend 函数
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
// 返回值不为 Response 的 suspend 函数。
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
这里理一下上面说到的 adapterType(有的地方代码也叫 returnType) 和 responseType。这里分两种情况,一种是协程方法,一种不是协程方法。我分别举个例子。
协程方法:
Kotlin
interface MyService {
@GET("/foo1")
suspend fun foo1(): String
@GET("/foo2")
suspend fun foo2(): Response<String>
}
协程方法中的 adapterType 固定是 Call(这里的 Call 是 Retrofit 中定义的,注意和 OkHttp 中的 Call 区分,Retrofit 中的 Call 接口和 OkHttp 中的 Call 差不多,只是简单封装了下)
上面 foo1() 和 foo2() 他们的 responseType 都是 String,foo1() 就是源码中的不需要 Response 的协程方法,而 foo2() 是需要 Response 的协程方法。
非协程方法:
Kotlin
interface MyService {
@GET("/foo1")
fun foo1(): Call<String>
@GET("/foo2")
fun foo2(): Single<String>
}
foo1() 的 adapterType 就是 Call,而 foo2() 的 adapterType 就是 Single,他们的 responseType 都是 String。
通过上面的例子我相信你已经能够分清不同情况下的 adapterType 和 responseType 了,然后我们分析一下源码中的逻辑。
协程方法:
- 如果返回值为
Response那么就是需要Response的协程方法,它的adatperType固定为Call,responseType为Response中的泛型类型。 - 返回值如果不为
Response,那么就是不需要Response的协程方法。adapterType固定为Call,responseType固定为协程方法返回值。 - 协程方法会在原来的注解上添加一个
@SkipCallbackExecutor注解,这个注解表明后续的回调在主线程(后面我们会看到这个逻辑),如果不加默认在OkHttp的Dispatcher中的线程。
非协程方法:
- 方法的返回类型就直接是
adapterType,返回类型的泛型类型就是responseType(准确的来说是CallAdapter返回的,但是几乎都满足这个条件。)
协程方法和非协程方法的通用逻辑:
- 获取
adapterType对应的CallAdapter,也就是通过CallAdapter获取对应adapterType的实例,这个CallAdapter就是用来描述如何构建请求任务的,我们可以自定义CallAdapterFactory来实现自定义的CallAdapter,来实现自定义的任务封装(比如自定义的RxJava和LiveData的任务封装),默认的CallAdapterFactory就只支持Call(Retrofit)。 - 如果是
HEAD请求,responseType必须为空。 responseType不能够是Reponse。 (包括OkHttp和Retrofit中的Response)- 获取
RequestBody转responseType的Converter。 - 根据不同的方法类型构建不同的
ServiceMethod实现。普通方法:CallAdapted;协程需要Resposne:SuspendForResponse;协程不需要Response:SuspendForBody。
OkHttpCall 的实现
上一节中说到,所有的方法类型的 ServiceMethod,都是由 CallAdapted、SuspendForResponse 和 SuspendForResponse 这三个对象实现,而他们的共同的父类就是 HttpServiceMethod,而 HttpServiceMethod 的父类是 ServiceMethod。
最终的方法调用是通过 ServiceMethod#invoke() 方法,这是一个抽象方法,实现是 HttpServiceMethod#invoke() 方法,我们先来看看它的实现:
Kotlin
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
我们看到这个构建了一个 Call 对象,它的实现类是 OkHttpCall,这个和 OkHttp 中的 ReallCall 是类似的。enqueue() 方法是异步调用,execute() 是同步调用,如果 adapterType 是 Call,最后返回的 Call 的实现就是 OkHttpCall,后面我们分析 DefaultCallAdapterFactory 的代码的时候能够看到这部分代码。
OkHttpCall 同步调用
先看看同步调用的方法 OkHttpCall#execute() 方法的实现:
Java
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
// 构建 OkHttp 中的 Call
call = getRawCall();
}
if (canceled) {
call.cancel();
}
// 解析 OkHttp 的 Response 为 Retrofit 的 Response
return parseResponse(call.execute());
}
通过 getRawCall() 方法来构建 OkHttp 中的 Call,同步执行 Call#execute() (OkHttp),然后通过 parseResponse() 方法取解析 OkHttp 中的 Response 为 Retrofit 中的 Response。
先看看 getRawCall() 方法的源码:
Java
@GuardedBy("this")
private okhttp3.Call getRawCall() throws IOException {
okhttp3.Call call = rawCall;
// 如果已经创建 OkHttp.Call 直接使用。
if (call != null) return call;
// Re-throw previous failures if this isn't the first attempt.
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
// Create and remember either the success or the failure.
try {
// 创建 OkHttp.Call
return rawCall = createRawCall();
} catch (RuntimeException | Error | IOException e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
创建 OkHttp.Call 是通过 createRawCall() 方法,继续看看实现:
Java
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 的实现就是 OkHttpClient,其实就是调用了 OkHttpClient#newCall() 方法创建了一个 RealCall;而 RequestFactory,就是我们在第一篇文章和第二篇文章中介绍的,解析各种注解后,用来生成 OkHttp.Request 的对象,我们看到它把参数传递给了 RequestFactory#create() 方法,然后生成了 OkHttp.Request 对象,我们在看看它的源码实现:
Java
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
// 获取所有的参数处理器
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args.length;
// 参数的数量不正确,报错。
if (argumentCount != handlers.length) {
throw new IllegalArgumentException(
"Argument count ("
+ argumentCount
+ ") doesn't match expected count ("
+ handlers.length
+ ")");
}
// 构建一个 RequestBuilder
RequestBuilder requestBuilder =
new RequestBuilder(
httpMethod,
baseUrl,
relativeUrl,
headers,
contentType,
hasBody,
isFormEncoded,
isMultipart);
// 如果是协程方法,需要参数数量减一。
if (isKotlinSuspendFunction) {
// The Continuation is the last parameter and the handlers array contains null at that index.
argumentCount--;
}
List<Object> argumentList = new ArrayList<>(argumentCount);
// 遍历参数处理器,填充请求的数据到 RequestBuilder 中。
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
// 构建 okHttp.Request
return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
简单描述一下:
- 检查
ParameterHandler的数量和args的数量是否一致。 - 构建
RequestBuilder对象。 - 如果是
suspend函数,需要把参数的数量减一,原因看前面我对suspend函数的描述。 - 遍历
ParameterHandler,填充请求的数据到 RequestBuilder 中。 - 构建
Request。
再看看解析 OkHttp.Response 的 parseResponse() 方法实现:
Java
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
// 提取 Response Body
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
// 构建新的 Response,但是移除了 ResponseBody
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
// 不为 200+,请求都表示失败
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();
}
}
// 204,205 RequestBody 固定为空
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
// 构建新的 ResponseBody,拦截读取过程中的异常
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 通过 Converter 将 ResponseBody 转换成 responseType
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.
// 抛出读取 ResponseBody 时发生的错误。
catchingBody.throwIfCaught();
throw e;
}
}
总结一下解析 RepsonseBody 的流程:
- 提取
ResposneBody,构建一个新的Response,但是移除ResponseBody。 Response Code不为 200+ 都表示请求失败。Response Code为 204 或者 205,不读ResponseBody。- 通过
Converter,将ResponseBody转换成responseType。
OkHttpCall 异步调用
直接看 OkHttpCall#enqueue() 的源码:
Java
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
// 创建 `OkHttp.Call`
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
// 回调 `OkHttp.Call` 创建失败
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// OkHttp.Call 的异步调用
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 解析 Response
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
// 回调成功
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
// 回调失败
callFailure(e);
}
private void callFailure(Throwable e) {
try {
// 回调失败
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
异步调用的代码也是通过 createRawCall() 方法来生成 OkHttp.Call,也是通过 parseResponse() 方法来解析 OkHttp.Response,这两个关键方法在同步调用时都分析过了,异步调用的代码也非常简单,大家自己看看就行。
OkHttpCall 总结
OkHttpCall 也就是 Retrofit 中对 Http 请求的核心封装,默认的 DefaultCallAdapterFactory 的实现也是直接返回 OkHttpCall 当 Call 的实现。像其他的任务封装,例如 RxJava、LiveData 等等,也都是要基于 OkHttpCall 的实现的变形。
CallAdapted 的实现
代理的方法调用通过调用 ServiceMethod#invoke() 方法实现,HttpServiceMethod#invoke() 方法中又会创建一个 OkHttpCall 对象(前面分析了),然后调用 adapt() 方法将这个对象传递给它的实现类,adapt() 是一个抽象方法,CallAdapted,SuspendForResponse 和 SuspendForBody 他们分别实现了不同的 adapt() 方法。
看看 CallAdapted#adapt() 方法的实现:
Java
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
直接调用 callAdapter 的 adapt() 方法,这个 callAdapter 是根据 adapterType 从 CallAdapterFactory(它可以在构建 Retrofit 实例的时候自定义) 中获取的。我们的 Call 类型的 adapterType 就是直接从 DefaultCallAdapterFactory 中获取的 callAdapter。
直接看看 DefaultCallAdapterFactory#get() 方法的实现:
Java
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
// adapterType 必须为 Call
if (getRawType(returnType) != Call.class) {
return null;
}
// Call 中必须有具体的泛型类型
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
// 获取 ReponseType
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
// 判断是否有 @SkipCallbackExecutor 注解,如果有就表示需要在主线程回调,前面在分析协程那部分是提到过。
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
// 构建匿名的 CallAdapter 对象
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
// 如果不需要在新的线程中回调,直接返回 OkHttpCall,反之用ExecutorCallbackCall 封装一下。
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
总结一下上面的逻辑:
adapterType为Call表示当前的CallAdapterFactory能够处理,反之不能处理,返回为空,系统就会查找下一个能够处理的CallAdapterFacotry。- 获取
Call的泛型类型作为responseType。 - 如果有
@SkipCallbackExecutor注解(讲协程方法时有讲到),表示需要在主线程回调。 - 构建一个匿名的
CallAdapter对象,其中的adapt()方法如果需要在主线程回调,就需要用ExecutorCallbackCall来封装一下OkHttpCall,反之直接返回OkHttpCall,adapt()方法的返回值也是最终ServiceMethod#invoke()方法的返回值,也是最终代理的方法的返回值。
SuspendForResponse 的实现
我们直接看 SuspendForResponse#adapt() 方法的实现:
Java
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
// 这个 callAdapter 一定是由 DefaultCallAdapterFactory 生成的,因为协程方法的 adaptType 一定是 Call
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
// 获取协程方法必须的 Continuation 参数
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
// See SuspendForBody for explanation about this try/catch.
try {
// 调用协程方法,这个实现的代码是 Kotlin
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
总结一下上面的代码:
callAdapter一定是由DefaultCallAdapterFactory生成的,因为协程方法的adaptType一定是Call。- 获取
Continuation参数,协程方法调用一定要它。 - 通过
KotlinExtensions.awaitResponse()方法调用协程方法,这是由Kotlin写的。
继续看看 KotlinExtensions.awaitResponse() 源码:
Kotlin
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)
}
})
}
}
如果不知道 Callback 转 suspend 方法的同学,建议网上去找找资料,直接通过 OkHttpCall#enqueue() 方法触发请求的,然后转化成了协程 suspend 方法,注意这里直接返回的 Response(Retrofit)。
SuspendForBody 的实现
SuspendForBody#adapt() 方法的实现:
Java
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
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);
}
}
几乎可以说是和 SuspendForResponse 中的代码一模一样,只是加了一个空的判断,最终不为空的协程方法调用是调用的 KotlinExtensions.await() 方法,我们看看它的实现:
Kotlin
@JvmName("awaitNullable")
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) {
continuation.resume(response.body())
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T?>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
实现基本也是和 KotlinExtensions.awaitResponse() 一样,只是最后的返回值是取的 ResponseBody (转换成 returnType 的 ResponseBody)。
最后
Retrofit 的 Http Request 的构建和 Http 请求任务的构建两个核心逻辑也都跑通了,基本整个流程也都串起来了,后续准备再写一篇文章来介绍 Moshi 的 ConverterFactory 和 RxJava 的 CallAdapterFactory 是如何工作的,给想自定义 ConverterFactory 与 CallAdapterFactory 的同学一些参考。