1. 序
基于retrofit 2.9.0
Retrofit并不是一个网络请求库,它主要是为了简化网络请求的构建和对返回数据的处理,网络请求是通过OkHttp实现的,而Retrofit仅负责网络请求接口的封装。在2.6.0版本之后,retrofit对kotlin协程进行了支持。
Retrofit的基本使用流程很简洁,为了实现这种简洁的使用流程,Retrofit内部使用了优秀的架构设计和大量的设计模式,它的源码非常值得我们一读。
2. 基本使用流程
- 定义API接口用于描述请求
1
2
3
4public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } - 创建
Retrofit并生成API的实现(注意:方法上面的注解表示请求的接口部分,返回类型是请求的返回值类型,方法的参数即是请求的参数)
1 | |
- 调用API方法,生成Call,执行请求
1 | |
阅读Retrofit源码前,需要对以下设计模式有所了解:
1 | |
同时需要对OkHttp源码有一定的了解,可以看上一篇OKHttp源码分析
3. 源码分析
按照上面的基本使用流程,我们来进行逐步分析内部是怎么实现的。
3.1 Retrofit构建过程
我们先来看看Retrofit内部结构:
1 | |
可以看到Retrofit中有一个全局变量serviceMethodCache,它是一个ConcurrentHashMap,内部以Method为键ServiceMethod为值,这个ServiceMethod就是我们之前定义的api接口中方法注解进行解析得到的。内部还有一个静态内部类Builder,通过建造者模式来构建需要不同配置的Retrofit实例,Builder在调用build()方法创建Retrofit对象时对很多配置进行了默认赋值。
我们看到builder内调用了Platform.get()方法,来看看它的实现:
1 | |
可以看到Platform内部针对Android和Java平台做了不同的实现。
3.2 创建GitHubService
我们来看GitHubService service = retrofit.create(GitHubService.class):
1 | |
可以看到retrofit.create方法内部通过动态代理来创建了GitHubService的实例,在我们调用GitHubService中的方法时就会先执行InvocationHandler的invoke方法。
继续loadServiceMethod:
1 | |
ServiceMethod.parseAnnotations内部最终调用了RequestFactory.parseAnnotations和HttpServiceMethod.parseAnnotations,我们分别来看下这两个方法实现:
1 | |
RequestFactory主要是来解析方法的注解参数等,也是通过建造者模式创建RequestFactory,我们可以看到RequestFactory.parseAnnotations主要做了三件事:
- 首先通过parseMethodAnnotation解析注解,获取它的HTTP方法类型,HTTP的Header,HTTP地址相对路径,需要填充的地址绝对值,是否有body等信息。
- 通过ParameterHandler的parseParameter来解析参数的注解,这里使用的是Path,2个参数都有注解,则会创建2个ParameterHandler.Path放进parameterHandlers中来记录参数的注解信息
- 信息解析完毕返回RequestFactory对象
接着来看HttpServiceMethod.parseAnnotations:
1 |
|
我们来看@1注解createCallAdapter实现:
1 | |
找到一个请求适配器CallAdapter,规则是在前面声明的请求适配器中找到一个能够处理这个返回类型的CallAdapter,我们GitHubService中方法申明返回Call,则会使用系统添加的DefaultCallAdapterFactory来处理,如果方法声明用RXJava来处理,则需要添加一个RXJava的适配器CallAdapter。
注释@2中的createResponseConverter实现基本上和createCallAdapter一样,这里我们就不在展开了。
接着我们来看loadServiceMethod(method).invoke(args):
上面已经说过loadServiceMethod(method)最终会返回一个ServiceMethod,ServiceMethod在执行invoke方法时是实际上调用的是HttpServiceMethod.invoke方法:
1 | |
内部创建了一个OkHttpCall接着调用了adapt方法:
1 | |
可以看到Retrofit内部最终还是通过Okhttp取真正执行网络请求的。
接着看adapt(call, args),我们上面看到了adapt是HttpServiceMethod中的一个抽象方法,非Kotlin协程的调用实际是调用了CallAdapted.adapt方法:
1 | |
可以看到内部调用了callAdapter.adapt(call)方法,这个callAdapter就是我们之前调用createCallAdapter方法找到的(见HttpServiceMethod.parseAnnotations内部注释)。它的默认实现实际上是在DefaultCallAdapterFactory中:
1 | |
可以看到我们之前创建的OkHttpCall经过adapt方法后Call会被包装为ExecutorCallbackCall类型,这儿使用了装饰模式。然后返回此对象。ExecutorCallbackCall的作用在于包装OkHttpCall,把它的返回结果放到主线程执行。
3.3 执行网络请求
1 | |
通过前面的分析知道,此时的Call对象实际上是ExecutorCallbackCall,执行请求时内部实际上调用的是OkHttpCall中方法,而之前我们讲OkHttpCall内部实现时讲过,它实际上是通过Okhttp去真正执行网络请求的。
到此Retrofit的正常流程就分析完了,接下来我们看看Retrofit是如何适配kotlin协程的。
3.4 适配kotlin协程
我们把上面分析过的HttpServiceMethod.parseAnnotations拿过来,这次分析协程逻辑:
1 |
|
可以看到如果方法是kotlin的挂起函数,我们会根据不同的返回值类型来创建SuspendForResponse或者SuspendForBody。只不过SuspendForResponse获取的返回值是response而SuspendForBody获取的是response.body(),我们直接来看SuspendForBody内部实现:
1 | |
首先第一步,适配Call,如果是RxJava,这里的callAdapter就是RxJava2CallAdapter,同时返回的就是Observable,而协程的callAdapter跟我们之前正常流程一样,是Retrofit默认的DefaultCallAdapterFactory。
1 | |
接着我们看下面的返回:
1 | |
这里的isNullable目前Retrofit的版本都是false,可能后续会支持空类型。但现在肯定是不支持的,所以直接进入KotlinExtensions.await():
1 | |
可以看到上面的扩展函数通过suspendCancellableCoroutine来创建一个协程,这个协程被取消时会回调invokeOnCancellation 方法,我们可以在这儿做一些释放资源的操作。主要是onResponse回调,协程通过挂起来执行耗时任务,而成功与失败会分别通过resume()与resumeWithExecption()来唤起挂起的协程,让它返回之前的挂起点,进行执行。而resumeWithExecption()内部也是调用了resume(),所以协程的唤起都是通过resume()来操作的。调用resume()之后,我们可以在调用协程的地方返回请求的结果。
4. 总结
下面我们用一张图来总结一下Retrofit的核心流程:
