框架解读 | Retrofit设计剖析
Retrofit是一个类型安全的HTTP客户端,可以通过注解将HTTP API转换为Java接口,并使用动态代理,CallAdapter和Converter来发起请求和解析响应。
本文 着重于 Retrofit的架构设计,对于其
注解解析能力
上 不作详细阐述本文基于
retrofit:2.6.2
在上文# 框架解读 | OkHttp设计剖析中我们讲完了OkHttp,那本文就来讲讲与其经常搭配使用的Retrofit
示例
本示例仅以最基础的retrofit发起请求为例,关于kotlin suspend或rxjava等封装,会在源码阶段进行一同剖析
// 定义一个接口,用注解描述HTTP请求
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
// 创建一个Retrofit实例,并指定基础URL和转换器
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);
// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
// 处理响应数据
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
// 处理失败情况
}
});
源码剖析
通常在retrofit的使用中,会结合rxjava或者kotlin suspend关键字一同使用,但与示例代码无异的是这两步:
- 得到Service对象
- 调用Service方法
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);
// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
而对于retrofit,我们最好奇的无非是其如何联动的OkHttp,借助其发出请求。OkHttp中发起请求是通过Call.enqueue()
,那本文的目标方向就是这个方法的调用
得到Service对象
retrofit.create
得到的是由动态代理newProxyInstance
生成的Service对象,重点在于其中的invoke
方法,对于通常情况,最终调用的都是loadServiceMethod(method)
执行的 invoke(),那接着看看它返回了什么对象,并且其 invoke() 究竟做了些什么
// Retrofit.java
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
Service的每个方法最终都对应着一个HttpServiceMethod
,而其也有几个实现类CallAdapted
(通常情况)、SuspendForResponse
(suspend使用)、SuspendForBody
// Retrofit.java
ServiceMethod<?> loadServiceMethod(Method method) {
// 这里还做了缓存,这里注意喔,缓存的是method,而不是service对应的class
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 最终return的是这个result,跟进去看看
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
// ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 这里进行了Request注解方面的解析,感兴趣的可以跟进去看,重点关注其中的`parseMethodAnnotation()`和`parseParameter()`
// 内部区分了`methodAnnotations`和`parameterAnnotations`两种类型注解
// 最终封装得到RequestFactory,也就转化为了okhttp请求时需要的一些内容
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// ……
// 继续往下看,其实返的就是HttpServiceMethod对象
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
// HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
Annotation[] annotations = method.getAnnotations();
Type adapterType;
// ……
// 虽然suspend会有些区别,但你完全可以理解为 方法 return的对象类型
// 比如联动rxjava,就会返回一个Observable<T>
adapterType = method.getGenericReturnType();
// ……
// Call适配器,Call其实就是OkHttp的Call,适配器的意义就是将其转化为对应的类型,比如rxjava的Observable
// 但其内部仍会调用Call的方法
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
// 这个是结果解析器,gson就是添加在这里
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.
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);
}
}
前面讲到了invoke()
,我们继续。对于HttpServiceMethod
的几个子类,均没有实现该方法,发现在其父类中,转而调用了adapt
,留给其子类去实现
@Override final @Nullable ReturnT invoke(Object[] args) {
// 这个Call不是OkHttp的Call
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
// 其实在call的设计中,就完全能看到okhttp的影子了
public interface Call<T> extends Cloneable {
/**
* Synchronously send the request and return its response.
*
* @throws IOException if a problem occurred talking to the server.
* @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
* or decoding the response.
*/
Response<T> execute() throws IOException;
/**
* Asynchronously send the request and notify {@code callback} of its response or if an error
* occurred talking to the server, creating the request, or processing the response.
*/
void enqueue(Callback<T> callback);
/**
* Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
* #enqueue(Callback) enqueued}. It is an error to execute or enqueue a call more than once.
*/
boolean isExecuted();
/**
* Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not
* yet been executed it never will be.
*/
void cancel();
/** True if {@link #cancel()} was called. */
boolean isCanceled();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call<T> clone();
/** The original HTTP request. */
Request request();
}
调用Service方法
在create
的动态代理细节中我们得知,retrofit service每个方法的调用,都将映射到对应类型的HttpServiceMethod.adapt()
普通调用
// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
// 处理响应数据
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
// 处理失败情况
}
});
最普通的情况,返回值为原始Call,那就对应的是CallAdapted
,然而这里令人头疼的是,它又套了一层。这里设计的原因就是为了兼容如Rxjava等场景
// CallAdapted.java
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
那这些callAdapter
来自于哪里呢?回到前面传入的地方
callAdapterFactories()
>>> retrofit.callAdapter(returnType, annotations)
>>> nextCallAdapter(null, returnType, annotations)
// nextCallAdapter()
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
// 可以想象的是,callAdapterFactories会根据returnType去匹配给到对象的adpter
// 至于这个,对比下rxjava联动时需要添加的代码.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// 也就是说Retrofit在建造过程中,会逐步的传进去
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
// Retrofit.Builder.build()
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
那对于最普通的这种调用,那必然就是DefaultCallAdapterFactory
了,而得到adapter最终是ExecutorCallbackCall
// DefaultCallAdapterFactory.java
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);
}
};
}
回顾下示例代码,调用service method
得到Call
后,调用其enqueue
,那也就是说最终走入了ExecutorCallbackCall.enqueue
// ExecutorCallbackCall.java
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// 这里的delegate,其实就是构造时传入的`OkHttpCall`,当然这还不是实际OkHttp的Call
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
// 当结果回来时,需要进行线程切换
// 至于这个callbackExecutor感兴趣的可以自行追踪下,其实就是retrofit在build时生成的一个MainThreadExecutor
// 由platform.defaultCallbackExecutor()得到,实际内部就是个handler……
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
那请求其实还是由OkHttpCall
发起,中间的无论HttpServiceMethod
还是CallAdapter
,无非就是为了扩展性和兼容性进行的设计罢了
// OkHttpCall.java
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(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 {
// 这里由callFactory.newCall生成
// 而这个callFactory就是retrofit.client(okHttpClient)
// 到这明白了吧,这个OkHttpCall终究还是OkHttpClient.RealCall套了一层壳
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// 下面的内容,对于熟悉OkHttp的小伙伴来说已经毫无悬念了
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 这里进行了结果的解析,其中用到了`responseConverter`,也就是addConverterFactory
// 同样的,也是类似于CallAdapter的设计
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
}
}
});
}
其实到这,Retrofit的大体设计已经结束了。其中HttpServiceMethod
和CallAdapter
是其中的关键
Rxjava调用
书接上文普通调用时,对于Rxjava的联动时,区分点在于CallAdapter
addCallAdapterFactory(RxJava2CallAdapterFactory.create());
同样去看factory.get
会得到RxJavaCallAdapter
// RxJavaCallAdapter.java
@Override public Object adapt(Call<R> call) {
// 这里根据同步\异步 将OkHttpCall传了进去,结合rxjava操作符,发起okhttp请求,并把结果发射出去
// 可以看对应类中的call(),这里就不展开了蛤,相信你已经可以独当一面了
// 当然,主要是我对rxjava操作符不太熟……
OnSubscribe<Response<R>> callFunc = isAsync
? new CallEnqueueOnSubscribe<>(call)
: new CallExecuteOnSubscribe<>(call);
OnSubscribe<?> func;
if (isResult) {
func = new ResultOnSubscribe<>(callFunc);
} else if (isBody) {
func = new BodyOnSubscribe<>(callFunc);
} else {
func = callFunc;
}
// 这里包装之后,最终返回了出去,得到了通常使用的Observable
Observable<?> observable = Observable.create(func);
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isSingle) {
return observable.toSingle();
}
if (isCompletable) {
return observable.toCompletable();
}
return observable;
}
Suspend调用
对于协程结合retrofit,通常就是scope.launch{ service.method() }
CoroutineScope(Dispatchers.IO).launch {
try {
val repos = service.listRepos("octocat")
// 处理响应数据,通常会给一个callback,外面再通过livedata去发射处理好的数据
} catch (e: Exception) {
// 处理异常情况
}
}
对于suspend,必然结合协程,也就取代了rxjava的线程切换能力,那HttpServiceMethod
对应SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody
,而CallAdapter
没啥花里胡哨的,得到的Call
就是最普通的ExecutorCallbackCall
// SuspendForResponse.java
@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 {
// 对于suspend的阻塞,await是再熟悉不过了的
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return KotlinExtensions.yieldAndThrow(e, continuation);
}
}
寻寻觅觅,蓦然回首那人却在灯火阑珊处,最终ExecutorCallbackCall
得到了调用,这里是个suspendCancellableCoroutine
的挂起使用,结合resume
进行唤起,关于这点想要了解的可以看这篇# Android小记:Kotlin协程suspend的机制解读
suspend fun <T : Any> 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)
}
})
}
}
总结
其实对于Retrofit的设计最耐人寻味的莫非是通过动态代理
机制配合HttpServiceMethod
,实现了在method
的颗粒度上完成了扩展设计,而CallAdapter
、Converter
无异于在整体的兼容性和解耦上更加锦上添花。
这其实也给注解的适用场景提供了非常好的借鉴意义,通常可能会局限于APT去进行插桩或代码生成
• Retrofit使用动态代理机制,通过Proxy.newProxyInstance方法创建接口的代理类
并将所有方法调用委托给内部的InvocationHandler处理
• InvocationHandler负责根据方法的注解创建ServiceMethod对象
ServiceMethod是一个抽象类,它封装了请求的相关信息,如URL,HTTP方法,参数,请求体等
ServiceMethod的具体实现类是HttpServiceMethod
它根据Retrofit配置的CallAdapter和Converter来创建Call对象和解析响应数据
• CallAdapter是一个接口,它定义了如何将Call对象转换为其他类型,如Observable,Deferred等
Retrofit提供了默认的CallAdapter,也可以通过addCallAdapterFactory方法添加自定义的CallAdapter
• Converter是一个接口,它定义了如何将请求体和响应体转换为Java对象
Retrofit提供了一些内置的Converter,如GsonConverter
也可以通过addConverterFactory方法添加自定义的Converter