携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
Retrofit源码系列文章:
- 挖一挖Retrofit源码(一)
- 挖一挖Retrofit源码(二)
- Retrofit之CallAdapter解析
- Retrofit之自定义CallAdapter
- Retrofit之Converter解析
在上一篇挖一挖Retrofit源码(一)分析了Retrofit的初始化流程和创建动态代理对象的逻辑,本文将继续介绍Retrofit的请求和回调处理。
PS:本文基于Retrofit版本2.8.0
请求和回调处理
当接口方法被调用的时候,是通过动态代理对象调用invoke方法来发起请求的,实际的调用链路为loadServiceMethod.invoke -> ServiceMethod.invoke -> HttpServiceMethod.invoke
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
//传入OkHttpCall实例发起请求
return adapt(call, args);
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args); //①
复制代码
HttpServiceMethod的invoke方法里生成了一个OkHttpCall实例,并传进了①发起请求,前面2.3中HttpServiceMethod对象的生成是分了三种情况的,这里的adapt方法的具体实现也相对应分了三种情况:
- 接口方法不使用协程:adapt方法在HttpServiceMethod的子类CallAdapted中实现
- 接口方法挂起且返回类型为Response:adapt方法在HttpServiceMethod的子类SuspendForResponse中实现
- 接口方法挂起且为普通返回类型:adapt方法在HttpServiceMethod的子类SuspendForBody中实现
下面继续分情况来具体分析
1. 非挂起方法请求
查看一下CallAdapted中的adapt方法源码:
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
复制代码
可以看到,非挂起方法发起请求时实际上调用的是CallAdapter中的adapt方法,没有配置CallAdapter的话adapt方法默认是在DefaultCallAdapterFactory中实现的:
public Call<Object> adapt(Call<Object> call) {
return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
}
复制代码
adapt方法实际上返回了DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call))对象,继续点进去ExecutorCallbackCall里看看是啥东东:
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
this.delegate.enqueue(new Callback<T>() {
......
});
}
@Override public Response<T> execute() throws IOException {
return delegate.execute();
}
......
}
复制代码
入参delegate从上面已经知道了实际传进的是OkHttpCall,而非挂起方法进行异步请求时是调用了enqueue方法,同步请求则调用execute方法,也就是实际上请求和回调处理工作是由OkHttpCall.enqueue/OkHttpCall.execute完成的,ExecutorCallbackCall只是OkHttpCall的一个代理类。
1.1 OkHttpCall类enqueue方法
@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 {
call = rawCall = createRawCall(); //①
} catch (Throwable t) {
......//异常处理
}
}
}
......//省略生成call失败和取消的处理
//②
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
......//异常处理
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
......//异常处理
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e); //请求失败处理
}
});
}
复制代码
enqueue方法主要做了这些工作:
- ①处在网络请求前创建了OkHttp的Request对象,再将其封装成OkHttp.call
- ②处通过OkHttp的Call发起异步请求,在请求成功的回调里调用parseResponse将返回的数据解析成Retrofit的Response,最后将Response通过Callback.onResponse回调出去
- 请求失败则抛出异常
下面继续看看parseResponse这个方法是怎么把数据解析成接口方法所需的返回类型的。
1.2 OkHttpCall类parseResponse方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
......//根据状态码处理成功和异常的情况
//创建ResponseBody的代理对象
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
......//异常处理
}
}
复制代码
parseResponse方法里根据OkHttp返回结果类型生成了对应的响应体rawResponse,然后根据状态码对成功和异常的情况进行分别处理,并创建了一个ResponseBody的代理对象catchingBody,创建Retrofit实例时配置的数据转换器(GsonConverterFactory)通过代理对象catchingBody读取数据流将返回的数据转换成接口方法的返回类型(DataBean),然后把转换后的结果封装成OkHttp的Response类型并返回,最终在enqueue方法里调用Callback.onResponse将结果再封装成Retrofit的Response类型回调返回。
1.3 OkHttpCall类execute方法
@Override public Response<T> 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());
}
复制代码
同步请求的逻辑很简单,先创建出一个OkHttp的Request对象封装成OkHttp.Call,然后调用call.execute发起网络请求,并解析返回数据封装成Response类型返回。
2. 普通返回类型的挂起方法请求
@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);
}
}
复制代码
普通返回类型的挂起方法发起请求时实际上调用的是SuspendForBody中的adapt方法,方法内部先是走了一遍callAdapter.adapt创建出一个Retrofit的Response类型对象call,返回数据可空则调用KotlinExtensions.awaitNullable,否则调用KotlinExtensions.await来发起请求,awaitNullable方法和await方法的逻辑大同小异,下面看await方法即可:
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) {
......//响应体为空的异常处理
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
复制代码
suspendCancellableCoroutine会挂起协程,然后在enqueue方法里发起请求,这里实际上执行的是OkHttpCall的enqueue,使用OkHttp发起网络请求,请求成功并且回复成功则调用resume方法恢复挂起并返回数据,否则调用resumeWithException恢复挂起并抛出错误。
3. 返回类型为Response的挂起方法请求
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
try {
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
复制代码
返回类型为Response的挂起方法发起请求时实际上调用的是SuspendForResponse中的adapt方法,同样会先走一遍callAdapter.adapt,逻辑跟2中的大同小异,此处不展开分析了。
流程
Retrofit的简单调用流程:
PS:本文仅代表个人理解看法,如有不对的地方欢迎各位大佬不吝赐教