Retrofit是OkHttp的一个辅助工具,前者负责网络调度,后者负责网络执行。
网络请求的工作本质上是OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。
从一开始,Retrofit要提供的就是个Call工作对象。
public interface INetApi {
@GET("/lpy/api.php")
Call<Bean> getBeanInfo(@Query("id") String id);
}
这个接口是用来生产对象的接口,这个接口相当于一个工厂,接口中每个函数的返回值不是网络数据,而是一个能进行网络请求的工作对象,我们要先调用函数获得工作对象,再用这个工作对象去请求网络数据。
通过动态代理生成接口定义的网络请求对象
调用Retrofit.create()方法通过动态代理在InvocationHandler类中生成了Call网络工作对象——OkHttpCall。
调用serviceMethod.callAdapter.adapt(okHttpCall)来产生method所定义的返回(Gson、RxJava等)
new InvocationHandler() {
...
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
核心成员:ServiceMethod,它能准确的解析到接口中定义的函数,为最后的适配转换提供转换目标。
适配转换Call对象
其原理就是通过适配器CallAdapter把retrofit2.Call对象转换为目标对象。
初始化Retrofit对象时,不添加CallAdapterFactory也能实现适配转换。因为Retrofit使用OkHttpClient处理网络请求时会添加默认的callAdapterFactory,这个platform是一个简单工厂,能根据当前系统平台去生成对应的callAdapterFactory(Java8、Android或IOS)
public Retrofit build() {
...
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
//使用OkHttpClient处理网络请求
callFactory = new OkHttpClient();
}
...
//根据当前运行平台,设置默认的callAdapterFactory
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
函数解析、网络请求和数据转换
1. 函数解析
在接口函数里,用注解描述了输入参数,用Java对象定义了返回值类型,所以对输入参数和返回值,ServiceMethod采取了不同的方式去处理。
输入参数
输入参数是用来描述url的,ServiceMethod会根据反射得到的Method,取得Annotation注解信息,这些注解是Retrofit自己预定义好的(retrofit2.http.*),ServiceMethod根据预先的定义,直接判断注解所属的逻辑分支,在有网络请求时分情况进行处理,就能得到目标url,http请求头等数据。
返回值
返回值是需要用CallAdapter去适配的,所以核心在于生成对应的CallAdapter。
在Retrofit生成Call网络工作对象时,她通过动态代理获取到了接口函数的Method定义,从这个Method中可以获取函数定义的返回对象类型,由于这个转换是需要CallAdapterFactory生产CallAdapter对象去实现,而Retrofit事先并不知道要使用哪个Factory,所以她是遍历所有的CallAdapterFactory,根据目标函数的返回值类型,让每个Factory都去尝试生产一个CallAdapter,哪个成功就用哪个。
2. 网络请求
OkHttpCall继承的retrofit2.Call接口是为了依赖倒置解耦的,真正的网络请求是由OkHttpCall内部引用的okhttp3.call处理的,这个okhttp3.call是ServiceMethod获取的Retrofit中的callFactory,也就是Retrofit中的OkHttpClient。最终的网络请求是由OkHttpCall调用OkHttpClient发出的,调用和回调等过程,也就是在OkHttpCall中处理的。网络请求的生成过程中,为了使用接口函数中定义的参数,OkHttpCall会调用ServiceMethod来生成Request请求对象,再交给OkHttpCall去处理。
3. 数据转换
因为回调是在OkHttpCall中处理的,所以对回调数据的转换也在OkHttpCall中触发,为了符合接口函数中定义的返回数据类型,OkHttpCall会调用ServiceMethod.toResponse()方法来转换Response返回数据对象。
其他知识点
- Retrofit的实用价值意义在于,他能根据你的接口定义,灵活地生成对应的网络工作对象,然后你再择机去调用这个对象访问网络;
- Retrofit底层虽然使用了OkHttpClient去处理网络请求,但她并没有使用okhttp3.call这个Call接口,而是自己又建了一个retrofit2.Call接口,OkHttpCall继承的是retrofit2.Call,与okhttp3.call只是引用关系。
这样的设计符合依赖倒置原则,可以尽可能的与OkHttpClient解耦。 - addConverterFactory:扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另一种数据对象。
例如,GsonConverterFactory可以把Http访问得到的json字符串转换为Java数据对象Bean,这个Bean是在INetApi接口中要求的的; - addCallAdapterFactory:扩展的是对网络工作对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象;
- Retrofit使用动态代理生产网络请求对象是因为这种Call对象的生产需要有大量的配套代码,为了简化代码;
- 因为需要处理的方法和对象太多太复杂,需要使用建造者模式来把建造过程和使用过程分离开。
- Retrofit在类的单一职责方面分隔的很好,OkHttpCall类只负责网络交互,凡是需要知道函数定义的,都交给ServiceMethod类去处理,而ServiceMethod类对使用者不公开,因为Retrofit是个外观模式,而所有需要扩展的都在Retrofit的建造者中实现;
Q1、Retrofit具体是如何知道了INetApi中定义的Call网络请求对象,如何实现网络请求,以及如何执行的数据转换呢?
首先,根据接口中定义的函数,解析函数,得到函数的具体定义,并生成对应的ServiceMethod。
然后,根据这个ServiceMethod,实现一个OkHttpCall的Call对象,负责在Retrofit底层实现网络访问。
其中,在网络访问返回了网络数据时,根据ServiceMethod实现数据转换。
最后,利用匹配的适配器,把OkHttpCall对象转换为INetApi要求的Call网络请求对象。
Retrofit的好处
- 超级解耦
我们在请求接口数据的时候,API接口定义和API接口使用总是相互影响,什么传参、回调等,耦合在一块。有时候我们会考虑一下怎么封装我们的代码让这两个东西不那么耦合,这个就是Retrofit的解耦目标,也是它的最大的特点。
Retrofit为了实现解耦,使用了特别多的设计模式 - 可以配置不同HttpClient来实现网络请求,如OkHttp、HttpClient...
- 支持同步、异步和RxJava
- 可以配置不同的反序列化工具来解析数据,如json、xml...
- 请求速度快,使用非常方便灵活
更加详细的分析请见:https://www.jianshu.com/p/f57b7cdb1c99