Android Retrofit 源码流程分析(Retrofit 2.9.x 最新版本 附详细流程图)
前言
一直以来写博客的目的就是记录自己开发过程中的问题,顺带分享出来。尤其源码分析,很容易忘,写出来也方便以后回忆。阅读自己之前写的源码分析博客,大量图片(个人习惯截图代码段写上注释)源码跟踪跳来跳去流程很容易混淆,不够明了。这次借助分析最新Retrofit源码的机会,加入了流程图 (图片不清晰可以查看文末的网页链接),如下:
跟踪源码时经常会在源码文件中跳来跳去,一定不要忘记每个步骤跟踪源码时的目的,跟着流程图食用博客更佳!
一定要牢记 Retrofit 的核心是动态代理模式,当调用我们定义的代理类(ApiServices)方法时会回调到 InvocationHandler.invoke(),由于篇幅原因源码分析比较多,这里不再讲解动态代理相关知识。
示例代码
接口来源于 WanAndroid 开放 Api !!! Retrofit 版本
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
ApiService文件
interface ApiService {
@GET("article/list/0/json")
fun getArticle(): Call<ArticleBean>
}
Demo文件
val retrofit = Retrofit.Builder()
.baseUrl(Constant.API_BASE)
.addConverterFactory(MoshiConverterFactory.create())
.build()
val apiServices = retrofit.create(ApiService::class.java)
val call: Call<ArticleBean> = apiServices.getArticle()
call.enqueue(object : Callback<ArticleBean>{
override fun onResponse(call: Call<ArticleBean>, response: Response<ArticleBean>) {
//成功的回调
}
override fun onFailure(call: Call<ArticleBean>, t: Throwable) {
//失败的回调
}
})
上面的代码不用我多说,这就是最简单的Retrofit发起请求的例子。短短几行代码Retrofit便完成了发起请求、响应解析、线程切换的工作;那么当我们调用enqueue方法时Retrofit到底是如何工作的呢?下面将详细分析Retrofit的工作流程。
源码分析(多图来袭,图中对重点代码进行了注释)
由上图的代码分析,enqueue执行了请求,但是点进去发现是接口。ApiServices同样也是我们定义的接口。create() 方法传入了ApiServices,那么我们就从 create() 方法入手,点进去看源码,如下图: 先来看看方法中第一行的 validateServiceInterface,点进去看源码,如下图: validateServiceInterface方法主要对我们定义的ApiServices进行了一些错误检查。回过头我们继续看 create 方法,上上个图片中得知 create() 方法在 return 中调用了 loadServiceMethod(method).invoke(args),invoke我们点进去发现是个 ServiceMethod 接口,那么 loadServiceMethod 一定返回了他的实现类,我们点进去 loadServiceMethod, 如下图: 这段代码很简单,实现了一个 map 缓存,核心代码在于 ServiceMethod.parseAnnotations(this, method),我们点进去继续跟踪,如下图: 继续跟踪 HttpServiceMethod.parseAnnotations 如下图: 同时在 HttpServiceMethod 类中发现了 invoke 的实现: 以我们最初的 Demo 代码段为例,并没有写在 Kotlin 协程方法中,所以会进入 if (!isKotlinSuspendFunction) 判断,返回一个 CallAdapted 对象。 由以上这些信息可以推断出:
- Retrofit.loadServiceMethod() 实际返回的是一个 CallAdapter 对象。
- Retrofit.create() 方法最后调用的 loadServiceMethod(method).invoke(args) 调用到了 HttpServiceMethod.invoke(),而 HttpServiceMethod.invoke() 又调用了自身的 adapt()。
那么就相当于 loadServiceMethod(method).invoke(args) 调用到了 CallAdapted 的 adapt()。这一通调用流程搞通后,我们来看看 CallAdapted 的 adapt(),如下图: CallAdapted 类中包含一个 callAdapter,而这个 callAdapter 的赋值在构造器中,CallAdapted是在 HttpServiceMethod.parseAnnotations 方法中被new出来的,我们回看HttpServiceMethod.parseAnnotations的代码: callAdapter 是由 createCallAdapter 方法返回,我们点进去看代码: createCallAdapter 方法又调用了 retrofit.callAdapter()方法,我们回到 Retrofit 类中查看 callAdapter 方法: callAdapter 是通过 Retrofit 类中的 callAdapterFactories 中取出的,接着看一下 callAdapterFactories 是什么: 是一个 List 容器,找一下他的初始化代码: 在 Retrofit.build() 方法中默认调用了 platform.defaultCallAdapterFactories() 添加了一个 CallAdapter.Factory,接着看一下 defaultCallAdapterFactories 的实现: 由源码可以看出,最后的三目表达式无论如何都构造了一个list返回 ,并且这个list 中添加了一个 DefaultCallAdapterFactory,看一下其内部实现: 他的 get 方法返回了一个匿名 CallAdapter 对象,其 adapt 方法则返回了一个 ExecutorCallbackCall 对象,点进去看他的实现: 注意上面代码中的 callbackExecutor,是切换到主线程执行,这块代码先留个疑问,在文章最后会再次分析他是如何初始化的。到这里为止,可以总结出实例代码中的 enqueue 实际调用的就是ExecutorCallbackCall 的 enqueue,其中的 delegate 变量需要注意下,是在 HttpServiceMethod 的adapt方法中传入的: 这个 call 是 OkHttpCall ,这里也先按下不表,跟了这么一长串的源码,先来稍微总结下整体的调用流程。
调用流程总结
- 首先由动态代理入手,从Retrofit.create()源码开始分析,其返回了 loadServiceMethod(method).invoke(),由于 invoke 是一个接口,我们需要找出 loadServiceMethod 返回的具体是什么。
- loadServiceMethod 内部调用了 ServiceMethod.parseAnnotations ,parseAnnotations 调用了 HttpServiceMethod.parseAnnotations,通过其代码分析出返回了一个 CallAdapted 对象。由此分析出第一步中调用的 invoke 方法即为 CallAdapted 的 invoke。
- CallAdapted 的 invoke 方法调用了其内部变量 callAdapter 的 adapte 方法,接着继续从源码查找 callAdapter 是如何初始化的。
- 在 Retrofit 类中发现 callAdapter 是通过内部变量 callAdapterFactories 容器中取出并且调用其 get 方法获得。
- 接着在 Retrofit 的 build 方法中发现 callAdapterFactories 默认会添加一个 DefaultCallAdapterFactory 类型的元素,其 get 方法实现返回了一个匿名的 CallAdapter 对象,其 adapt 方法返回了一个 ExecutorCallbackCall 对象。由此得出示例代码中执行请求的enqueue方法最终会调用到 ExecutorCallbackCall 对象的 enqueue 方法。
- 在 ExecutorCallbackCall 的 enqueue 方法中实现了发起请求触发回调以及线程的切换。
看完这一系列的调用流程后,必然有两个疑问:
- 上面的源码分析并没有看到对 OkHttp 的封装,Retrofit 是如何封装 OkHttp 发起请求的?
- Retrofit 的线程切换存疑,在 ExecutorCallbackCall 的 enqueue 方法中触发回调时 callbackExecutor 为什么是切回到主线程?
下面来针对这两个问题再做一系列的源码分析!
Retrofit 是如何封装 OkHttp 的
先来分析下 Retrofit 时在哪里发起请求的,上面的源码分析得出 ExecutorCallbackCall 的 enqueue 是经过动态代理后实际调用的方法,回看一下他的实现: 在调用了 delegate.enqueue 后触发了回调,delegate 是一个 Call 类型,在构造器中初始化,这次逆着来找一下他究竟是什么?他的 enqueue 方法具体干了什么?开始跟踪源码: 是 CallAdapter 的 adapt 方法中以参数传入的,根据文章上面调用流程的分析 CallAdapter 的调用发生在 CallAdapted 中: 而 Calladapted 的 adapt 调用发生在他的父类 HttpServiceMethod 的 invoke 方法中,在这里发现了 call 的初始化,是一个 OkHttpCall,点进去查看 OkHttpCall 的 enqueue 方法: 在 OkHttpCall 的 enqueue 方法中已经开始初始化 OkHttp 的 Call 对象了,看一下 createRawCall 是如何初始化的: 创建 Call 对象的代码就一行,看起来简单,实际又要硬着头皮跟踪源码,这一行代码关键的就是两个内部变量 callFactory 和 requestFactory,先看一下他们各自的类型以及初始化: 两个变量都是在 OkHttpCall 的构造其中赋初值,也意味着又要跟踪源码,先来看看 callFactory,他说一个 okhttp3.Call.Factory 类型,熟悉 OkHttp 的同志肯定记得,OkHttp 的使用需要先创建一个 OkHttpClient 然后新建一个 Request 对象,再用 OkHttpClient 将 Request 传入,就会得到一个 Call 对象就可以发起请求触发回调了,而 OkHttpClient 正是 okhttp3.Call.Factory 的子类,这么一说就明白了,callFactory 一定类似 OkHttpClient 是用于创建 OkHttp 的 Call 对象,而 requestFactory 大致就可以推测出他是用于构建 OkHttp 的 Request 对象的。 先来跟踪 callFactory 的初始化源码,OkHttpCall 对象是在 HttpServiceMethod.invoke 方法中创建,而 HttpServiceMethod 对象根据文章上面调用流程分析是其内部静态方法 parseAnnotations 创建,再来看下他的源码: callFactory 传入的是 Retrofit 的 callFactory (这里也顺带找到了 requestFactory 的传入由方法参数传入),再接着回到 Retrofit 类中找寻 callFactory 的初始化: 由源码得知,callFactory 默认实现就是 OkHttpClient 跟之前的推测是一致的,了解了 callFactory 后再回过头看 requestFactory ,requestFactory 上面也看到是在 HttpServiceMethod 的静态方法 parseAnnotations 中以参数的形式传入,HttpServiceMethod.parseAnnotations 之前在 ServiceMethod 中看到过,回看 ServiceMethod 的代码: requestFactory 是在 ServiceMethod 中创建,继续跟踪 RequestFactory.parseAnnotations : 继续跟踪 build() 方法: 这个方法的核心逻辑遍历了传入方法的注解,并且调用了 parseMethodAnnotation,看一下 parseMethodAnnotation 的实现: 看到这里应该很眼熟,if else 判断的注解就是 ApiServices 定义的方法的请求方式注解,parseMethodAnnotation 方法的作用就是解析 ApiServices 中方法的各种注解信息,并且保存在内部变量中,回到 build() 方法看返回了什么: 其实就是返回了一个包含注解解析结果的 RequestFactory。 了解了 callFactory 和 requestFactory 的作用后回到 OkHttpCall 类: 看一下 requestFactory.create 的实现: 最终是返回了一个 okhttp3.Request。到这里已经实现了 OkHttp Call 的创建,接着回到 OkHttpCall 的 enqueue 方法 继续往下看: 再详细看一下 call.enqueue 的详细代码: 由此得知,Retrofit 对 OkHttp 的封装核心代码是 OkHttpCall 类,OkHttpCall 接受了子类传入的 Retrofit 的回调,内部创建了 OkHttp Call 对象并发起请求,在 OkHttp Call 请求的回调中再回调给 Retrofit 回调。
Retrofit 线程切换原理
Retroift 切换线程主要有两次切换,从主线程切换到子线程异步发起网络请求,请求结束后的回调中由子线程再切回到主线程触发回调,下面来详细分析两次线程切换的实现。
异步发起请求
由上面对 Retrofit 对 OkHttp 封装的分析可以得知,实际发起请求是通过 OkHttp 的 Call 对象的 enqueue 方法,该方法 OkHttp 内部已经封装为异步进行,所以从主线程切换到子线程异步发起网络请求是由 OkHttp 实现的,关于 OkHttp 的内部实现请关注我后面的文章,后面会写新的博客详细分析 OkHttp 的源码。
回调切回主线程
先回顾下发起请求后的回调操作: 由 OkHttpCall enqueue 方法中的这段代码得知,OkHttp 请求回调中的 callback 是 ExcuteorCallbackCall 传入,再次回看 ExcuteorCallbackCall 的 callback 代码: 很明显,切回主线程的主要功臣就是这个 callbackExecutor,他的初始化在构造器中,意味着又要跟踪源码找实现!根据最初分析的调用流程逆着找,首先是 DefaultCallAdapterFactory.get 中返回的匿名 CallAdapter 对象中创建了 ExcuteorCallbackCall: 由源码得知是将 executor 传给了 ExecutorCallbackCall, executor 的初始化又是 DefaultCallAdapterFactory 的构造器,还要继续跟踪。DefaultCallAdapterFactory 是由 Platform.defaultCallAdapterFactories 创建,看源码: callbackExecutor 以参数传入,继续逆着跟踪,defaultCallAdapterFactories 是在 Retrofit 类中 build() 方法中调用,继续看 Retrofit.build() 源码: 终于找到了 callbackExecutor 的初始化,点进去 platform.defaultCallbackExecutor 方法: 居然返回了个 NULL ??? 冷静分析,Platform 是处理平台兼容的类,从 Platform 入手来看下 Platform 在 Retrofit 上是如何初始化的: 在点进去 Android 类查看其是否重写 defaultCallbackExecutor 方法: 到这里总算明白了,ExcuteorCallbackCall 中回调的线程切换是由子线程切换到主线程执行回调方法,并且他的原理是通过 Handler 实现的。
流程图链接
个人使用 ProcessOn 随手画的 有些业余 https://www.processon.com/embed/6274eb155653bb45ea4b298d
写在最后
以上是本篇博客的全部内容,从 Retrofit 的调用流程入手详细分析了源码的执行过程、整体结构,在此基础上扩展分析了 Retrofit 线程切换原理以及如何实现对 OkHttp 的封装,当然 Retrofit 的源码远不仅如此,自定义 CallAdapter 结合 Rxjava、Kotlin 携程以及针对响应结果自定义解析的相关源码由于篇幅原因不在详细分析。 本篇博客的流程也是我个人学习 Retrofit 源码的过程,源码图片以及注释都是本人详细看过源码后标注的,流程图的制作也是本人原创(流程图画的有些业余),希望大家能多多支持点个赞,后面还会详细分析 OkHttp 的源码,OkHttp 以及 Retrofit 是当下 Android 最主流的两个网络请求库非常值得学习。
|