RetroFit2 源码学习相关
研究 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( |
以为这样很直观,逻辑也很清晰,实则 代码冗余,回调嵌套,如果有多个连续的请求,代码会变得难以维护,而 retrofit 搭配上协程能这样实现:
val data = apiService.getXXX(params) |
简单到不能再简单,动态代理
功不可没,上面的 apiService 是一个接口,由: retrofit.create(ApiInterface::class.java)
生成其实例,动态代理其核心实现:
public <T> T create(final Class<T> service) { |
由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) { |
上面代码关键变量isKotlinSuspendFunction
,用于判断是否为协程方法(suspend修饰),判断逻辑很简单,只需要判定方法最后一个参数是否为 Continuation.class
即可。这里的分叉逻辑都继承自HttpServiceMethod<T>
实现 ReturnT adapt(Call<ResponseT> call, Object[] args)
这个抽象方法,这也是 retrofit 使用 适配器模式的地方,把不同的调用方式进行统一。对于协程方式的调用有实现:
protected Object adapt(Call<ResponseT> call, Object[] args) { |
suspend fun <T> Call<T>.awaitResponse(): Response<T> { |
这里就一切都明朗了,实现了 Call
的扩展方法,这里的 Call
并不是 okhttp3.Call
,它只是 retrofit okhttp3.Call
为方便框架整体逻辑的处理而定义的,比如 retrofit 的 Call
是泛型化的,可以直接返回解析后的对象,enqueue
同理。
suspendCancellableCoroutine
方法是实现协程方法的关键,它可以将基于回调的异步操作封装成一个挂起函数,怎么理解呢?对 扩展方法awaitResponse
反编译可以看到方法定义是这样的:
public static final Object await(this$await, Continuation $completion) Call $ |
其实这里跟定义一个 listener
去监听方法的回调有点像,这个方法改写成 listener
的实现话大概就是这样:
fun <T> Call<T>.awaitResponse(listener:Listener<T>): Response<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( |
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