研究 retrofit 目标:理解动态代理、注解、反射、学习它所用到的设计模式,达到自己能手写它的核心实现。

最近终于有点精力能够去研究研究源码了, 真的是写的一个非常好的的开源库,以前刚接触安卓的时候扒拉过相关的源码,但是随着工作了几年之后,经验的积累,让我对源码里面的东西能够体会更深刻,自己也尝试去手写里面的核心实现,看完源码对整体的架构理解了之后,以为自己能很顺利的写下来,实则不然。
知识还是需要知行合一,这篇文章主要记录 retrofit 的一些知识点。

retrofit 的设计模式

retrofit 里面中使用了多种设计模式,以实现其灵活、可扩展和高性能的特性:

设计模式 应用场景 作用
建造者模式 Retrofit.Builder 灵活配置 Retrofit 实例
工厂模式 Converter.Factory、CallAdapter.Factory 创建 Converter 和 CallAdapter 实例
动态代理模式 接口方法转换为 HTTP 请求 运行时生成接口代理对象
适配器模式 CallAdapter 将 Call 适配为其他类型
装饰器模式 OkHttp 拦截器 增强 HTTP 请求和响应的功能
观察者模式 与 RxJava 或 LiveData 结合 实现异步数据流的订阅和通知
策略模式 Converter 和 CallAdapter 选择 动态选择数据转换或调用适配策略
单例模式 Retrofit 实例共享 确保全局只有一个 Retrofit 实例
模板方法模式 Call 的实现 定义 HTTP 请求的执行流程

retrofit 的动态代理模式

retrofit 用了诸多的设计模式,其中最经典的莫过于动态代理模式了,在了解 retrofit 之前,我一直以为这样的网络请求形式是最直观的,参考以前写的基于Volley框架的返回数据的范型处理

Request.get(
url = url,
params = param,
listener = object : OnRequestListener<Data> {
override fun onSuccess(commonData: CommonData?, data: Data?) {}
override fun onFailure(errorCode: Int, errorMessage: String?) {}
})

以为这样很直观,逻辑也很清晰,实则 代码冗余,回调嵌套,如果有多个连续的请求,代码会变得难以维护,而 retrofit 搭配上协程能这样实现:

val data = apiService.getXXX(params)

简单到不能再简单,动态代理功不可没,上面的 apiService 是一个接口,由:retrofit.create(ApiInterface::class.java) 生成其实例,动态代理其核心实现:

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 {
// ....
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}

由loadServiceMethod(method).invoke(args) 负责将接口方法(通过 Java 反射获取的 Method 对象)解析并转换为一个可执行的 HTTP 请求。

Proxy.newProxyInstance 方法,参数:

  • ClassLoader loader 用于加载代理类的类加载器。

  • Class<?>[] interfaces 代理类需要实现的接口数组,代理对象将实现这些接口,并拦截对这些接口方法的调用。只能代理实现了接口的类,不能代理没有接口的类。

  • InvocationHandler h
    调用处理器,负责处理代理对象上的方法调用。每次调用代理对象的方法时,都会调用 InvocationHandler 的 invoke 方法。对于 Retrofit 的接口我们并没有去“实现”它的方法,所有的逻辑都由` retrofit.create()方法里面返回的 InvocationHandler实现的 invoke方法实现的。

核心实现逻辑

协程的支持

Retrofit 支持多种异步编程模型,包括回调、RxJava 和协程等,这里主要记录一下对协程的支持。普通方法和异步逻辑的分叉在:

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.
//...
}

上面代码关键变量isKotlinSuspendFunction ,用于判断是否为协程方法(suspend修饰),判断逻辑很简单,只需要判定方法最后一个参数是否为Continuation.class 即可。这里的分叉逻辑都继承自HttpServiceMethod<T>实现 ReturnT adapt(Call<ResponseT> call, Object[] args)这个抽象方法,这也是 retrofit 使用 适配器模式的地方,把不同的调用方式进行统一。对于协程方式的调用有实现:

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 {
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return.suspendAndThrow(e, continuation);
}
}
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)
}
})
}
}

这里就一切都明朗了,实现了 Call的扩展方法,这里的 Call并不是 okhttp3.Call,它只是 retrofit okhttp3.Call为方便框架整体逻辑的处理而定义的,比如 retrofit 的 Call 是泛型化的,可以直接返回解析后的对象,enqueue同理。

suspendCancellableCoroutine方法是实现协程方法的关键,它可以将基于回调的异步操作封装成一个挂起函数,怎么理解呢?对 扩展方法awaitResponse反编译可以看到方法定义是这样的:

public static final Object await(@NotNull Call $this$await, @NotNull Continuation $completion) 

其实这里跟定义一个 listener去监听方法的回调有点像,这个方法改写成 listener的实现话大概就是这样:

fun <T> Call<T>.awaitResponse(listener:Listener<T>): Response<T> {
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
Listener.resume(response)
}

override fun onFailure(call: Call<T>, t: Throwable) {
Listener.resumeWithException(t)
}
})
}
}

可看到改造实现需要传递一个 listener,哪这个 listener是什么?前面其有如何判断一个方法是否为协程的方法的逻辑:判定方法最后一个参数是否为Continuation.class 即可。这里的 listener 其实可以等价于 一个 Continuation实例,kotlin 的协程库帮我们实现了对应的封装,对于使用我们不会直观地感受Continuation的存在,实际它贯穿整个协程。关于协程这里不再赘述,可以查看 《带着问题分析Kotlin协程原理》了解。

返回数据格式的解析

对于Converter,在协程和普通方法调用分叉逻辑的前面点:

Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);

createResponseConverter之后一路走到

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");

int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
}

converterFactories 的值就是在 retrofit 初始化的时候进行使用 public Builder addConverterFactory(Converter.Factory factory)添加的值。可以看到是按添加到List<Converter.Factory> converterFactories里面的顺序进行选择的,默认GsonConverterFactory实现了利用 Gson进行数据转化 ,如果我们自己实现Converter.Factory的接口的话,那么可以根据一定的规则判断是否要返回我们自定义的 Converter,如果不需要使用就返回 null,会自动匹配下一个能使用的 Converter。注意这里并不会因为前一个 Converter 解析失败而自动尝试使用下一个Converter(当然,你可以在自定义的Converter里面做类似这样的尝试策略)。

总结

  • 这篇文章深入剖析了 Retrofit 框架的核心设计模式、动态代理机制、协程支持以及数据解析逻辑,通过源码分析和手写实现,帮助读者更好地理解 Retrofit 的工作原理,并强调了理论与实践结合的重要性。

  • 为加深对 retrofit 的理解,可以尝试手写核心实现,自己尝试的的代码在 vmfit

  • 附一张 retrofit 的全流程图,来源:https://cloud.tencent.com/developer/article/1683334