在深入探究 Retrofit 的实现原理之前,我们先提出几个问题,后面带着这个几个问题去研究 Retrofit 的源码
- Retrofit 如何解析我们编写的注解接口,将其封装成网络请求
- Retrofit 默认接口返回类型是 Call ,配置 json convert 之后如何将返回类型替换成 Call {addConverterFactory}
- 在引入Kotlin协程之后,接口方法以 suspend 修饰,返回值直接修改成 Bean 类型,内部是如何实现的 {addCallAdapterFactory}
1. Retrofit的基本使用
Retrofit的官网提供的Retrofit使用的基本步骤
- 定义请求接口
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
复制代码
- 构建Retrofit实例, 获取接口实现类
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
复制代码
- 调用接口方法
Call<List<Repo>> repos = service.listRepos("octocat");
复制代码
- 调用返回的Call实例的
execute
或者enqueue
进行同步或异步请求
2. 原理分析
2.1 JDK动态代理
通过 JDK 提供的动态代理机制为我们的接口生成了具体的实现类。
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);
}
})
复制代码
生成的代理类会将所有方法交给内部的 InvocationHandler的inovke方法进行处理。
2.2 ServiceMethod
抽象类,代表我们定义的接口中的一个方法
- parseAnnotations 静态方法
- 解析方法的注解,获取 RequestFactory
- 通过 HttpServiceMethod 的静态方法进一步解析返回 ServiceMethod 的具体实现
2.3 HttpServiceMethod
抽象类,继承 HttpServiceMethod
- parseAnnotations 静态方法
- 获取 callAdapter
- 获取 responseConvert
- 创建 HttpServiceMethod 的最终实现类
2.3.1 CallAdapter
接口,对返回的结果进行包装,将内部传入的 Call (其实就是OkHttpCall) 转换成其他类型。
- Type responseType()
- 响应的类型 eg: Call的响应类型为Repo
- adapt(Call) T
默认为 DefaultCallAdapterFactory 中的匿名CallAdapter实现
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
} else if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
} else {
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType)returnType);
// 有没有SkipCallbackExecutor注解修饰 (debug 发现默认就有, annotations里面有这个注解)
// 有的话executor为null, 否则为 this.callbackExecutor
// 主要是用来设定回调的执行线程
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : this.callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
public Type responseType() {
return responseType;
}
public Call<Object> adapt(Call<Object> call) {
// executor为null返回原来的call
// executor不为null, 返回返回包装过的 ExecutorCallbackCall
// ExecutorCallbackCall 持有原本的call, 将回调Call.enqueue中的回调方法executor中执行, Android平台是
return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
}
};
}
}
复制代码
2.3.2 ResponseConvert
默认为 BuiltInConverters 处理 ResponseBody 返回。在构建Retrofit实例的时候可以通过addConverterFactory方法进行添加。
后面会分析到 Convert 作用的时机。
2.4 接口调用流程
根据上面的流程来看,Retrofit 会为每个接口生成一个代理对象,调用接口方法时进入代理对象内的 InvocationHandler 的 invoke 方法中,该方法中为调用的接口方法生成了 ServiceMethod 对象,并进行缓存,最后调用 ServiceMethod 对象的 invoke 方法获取结果。
ServiceMethod 的 invoke 方法为抽象方法,具体实现在 HttpServiceMethod 中。
复制代码
2.4.1 HttpServiceMethod#invoke
// 1. 创建一个 OkHttpCall (Retrofit)中的
// 2. 调用 adapt 方法
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
// 抽象方法 (具体子类中实现)
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
复制代码
HttpServiceMethod 类中有三个 static final 的子类,主要是非 Kotlin 协程 和 Kotlin 协程下的不同支持
2.4.2 CallAdapted
// 非Kotlin协程的接口
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
// 就是调用 CallAdapter 的 adapt 方法
return callAdapter.adapt(call);
}
}
复制代码
从上文介绍的 CallAdapter 可知,在没有手动 addCallAdapterFactory 的情况下,默认的 CallAdapter 是 DefaultCallAdapterFactory中创建的匿名对象,默认返回传入的 call。
2.4.3 OkHttpCall
实现 Retrofit 提供的 Call 接口,在未使用 Kotlin 协程的情况下调用接口方法返回的 Call 对象的实际类型。
- 成员变量
// okHttp中的Call
private @Nullable okhttp3.Call rawCall;
复制代码
- enqueue(Callback callback)
// callback 是 Retrofit 提供的回调
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
// OkHttp的Call
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 {
// 1. 创建 OkHttp 中的Call, 返回 RealCall
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
// ......
// 2. 利用OKHttp完成网络请求
call.enqueue(
new okhttp3.Callback() {
// 2.1 okHttp的成功回调
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
// 2.1.1 解析返回数据
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
// ...
}
// 2.1.2 调用 Retrofit 的回调接口
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
// ...
}
}
// 2.2 OkHttp的失败回调
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
// 2.2.1 调用 Retrofit 的回调接口
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
//...
}
}
});
}
复制代码
- createRawCall
private okhttp3.Call createRawCall() throws IOException {
// callFactory 就是 OkHttpClient
// newCall 返回 RealCall 实例
// requestFactory 构建 OkHttp 中的 Request 实例
// requestFactory 是 ServiceMethod 中静态方法 parseAnnotations 的返回结果
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
复制代码
- parseResponse
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
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();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 调用 responseConverter 解析返回数据
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.
catchingBody.throwIfCaught();
throw e;
}
}
复制代码
从 OKHttpCall 的实现可以看出,Retrofit 将底层的网路请求交给 OkHttp 进行处理,在 OkHttp 的 Callback 中解析并转换返回数据,同时回调用户通过 Retrofit Api 传递的网络请求回调。上文2.2.3中介绍的Convert就作用在这个解析的阶段。
2.4.4 协程suspend接口的处理
Retrofit 是使用Java编写的库,使用AS将我们编写的Kotlin接口进行反编译。
AS 中 code –> Decompile Kotlin to Java
// Kotlin suspend 接口
@FormUrlEncoded
@POST("book/borrow")
suspend fun borrowBook(
@Field("cardId") cardId: Long?,
@Field("bookIsbn") bookIsbn: String
): ApiResult<ApiResponse<BorrowBookResponse>>
// 反编译后的 Java 接口
public interface BookService {
@FormUrlEncoded
@POST(value="book/borrow")
@Nullable
public Object borrowBook(
@Field(value="cardId") @Nullable Long var1,
@Field(value="bookIsbn") @NotNull String var2,
@NotNull Continuation<? super ApiResult<ApiResponse<BorrowBookResponse>>> var3);
复制代码
可以看到其在最后给我们加了一个 Continuation 类型的参数,来标识这是一个 Kotlin 协程的接口。
在前文介绍的 ServiceMethod 的 parseAnnotations 静态方法进行解析的过程中,会对接口方法的参数进行解析,其中就会判断方法的参数类型是否为 Continuation 类型,如果是的话会将 isKotlinSuspendFunction 设置为true, 在后续的 HttpServiceMethod 类的 parseAnnotations 方法中会根据该值去构造具体的实现类。
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);
}
private @Nullable ParameterHandler<?> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler<?> result = null;
//。。。解析参数注解 省略
if (result == null) {
if (allowContinuation) {
try {
// 判断参数类型是否为 Continuation
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
复制代码
HttpServiceMethod#parseAnnotations方法的相关实现
// callFactory 其实就是 OkHttpClient
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.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
复制代码
可以看到在 isKotlinSuspendFunction 为false的情况下,构造的是前面介绍的 CallAdapted。而当其为 true 时有两个具体实现,主要区别是一个返回的包含 Response,另一个直接是 Body 的值,一般我们直接返回数据对象时都是 SuspendForBody。下面分析分析一下其内部实现。主要分析其 adapt 方法的实现
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call); // OKHttpCall
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);
}
}
// 接收者 Call
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
// 内部调用 Call 的 enqueue 方法
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)
}
})
}
}
复制代码
可以看到使用了 kotlin 协程之后框架帮我们调用的 Call 对象的enqueue方法并将响应的 body 返回给我们。
3. 问题解析
- Retrofit如何解析我们编写的请求接口?
- 在 ServiceMethod 类的 parseAnnotations 静态方法中调用 RequestFactory.parseAnnotations方法完成解析,并封装成 RequestFactory。
- Retrofit中的我们添加的Json Convert是如何生效的?
- 在 OkHttpCall 的 enqueue 方法中,内部会先调用 OkHttp 的 Call (实际为RealCall)完成网络请求,并在 RealCall 的回调方法中,调用了 Convert 的方法对网络返回的数据进行解析,此外还调用了 Retrofit中的传递的Callback方法。
- Retrofit中的CallAdapter的作用?
- CallAdapter 可以用来修改调用接口方法返回值类型,默认为 Call 类型,可以修改成其他类型,如 RxJava 中返回 Observable 类型(RxJava本人不了解)。
- 实现时可以提供一个自己的对象,内部持有 Retrofit 的 OkHttpCall,实际的请求仍然由 OkHttpCall 完成。
- 一般我们可以提供一个自己实现的Call类型,内部持有 OkHttpCall,在OkHttpCall的回调方法中对请求结果进行统一的封装之后,在调用外部 Callback 的 onResponse 和 onFailure 回调将封装后的结果带出去。
- 结合kotlin写成之后如何直接返回结果,不返回Call类型的?
- 结合上面的分析可知,框架内部帮我们用 Call 调用过 enqueue 进行请求了。