文章目录
八股一图流
一图流
Retrofit 对 OkHttp 的封装核心代码是 OkHttpCall 类,OkHttpCall 接受了子类传入的 Retrofit 的回调,内部创建了 OkHttp Call 对象并发起请求,在 OkHttp Call 请求的回调中再回调给 Retrofit 回调。
Retroift 切换线程主要有两次切换,从主线程切换到子线程异步发起网络请求,请求结束后的回调中由子线程再切回到主线程触发回调
三大流程
创建Retrofit对象
Retrofit使用建造者模式建立一个Retrofit实例,为其配置平台类型对象(Android对象,其中有一个主线程的线程池MainThreadExecutor)、网络请求的url地址、网络请求工厂(默认为OkHttpClient)、网络请求适配器的集合、数据转换器工厂的集合、回调方法执行器(默认为Android对象中的MainThreadExecutor)。
创建网络请求接口
Retrofit通过外观模式和代理模式使用create方法创建网络请求接口实例。在这方法中创建的是一个网络请求接口的动态代理对象,当通过这个代理对象调用接口中方法时,会执行动态代理对象的InvocationHandler的invoke()方法拦截,这样就实现了代理逻辑:interface中的⽅法全部由⼀个另外设定的InvocationHandler对象来进⾏代理操作。
在这个invoke方法中,会loadServiceMethod方法创建一个ServiceMethod,在这里面会根据接口方法的返回值和注解获得网络请求适配器、数据转换器…ServiceMethod相当于请求接口中的一个方法,几乎保存了一个网络请求所需要的数据。这里有个缓存操作,获取到ServiceMethod后会把它进行缓存,如果下次在获取直接从缓存里拿。然后会创建一个okHttpCall对象,网络请求对象,将其传入ServiceMethod的adapter方法中进行网络请求,根据网络适配器工厂确定返回的对象,比如call/observable。
发送网络请求
同步请求:同步请求中,会创建一个OkHtttp的Request对象再封装成call,然后调用call的execute方法发送请求,最后通过ServiceMethod的数据转换器对返回的数据进行解析。
异步请求:在异步请求中,也会创建一个Request并封装为call,然后进行网络请求,最后也是调用ServiceMethod的数据转换器进行返回数据的解析。他和同步请求方法不同的地方在于它其中牵扯了线程切换。请求完毕后会将回调方法交给回调执行器,它在里面获取了主线程的handler,通过这个handler切换到主线程
涉及的设计模式
- 建造者模式:在创建Retrofit对象时通过如下代码:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("xxx")
.build();
- 外观模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。Retrofit中这个统一的对象就是Retrofit。
- 代理模式:创建接口实例的时候就通过动态代理来对接口中的方法及其注解进行解析。
- 单例模式:获取Platform对象时使用到了单例:
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
}
- 装饰模式:动态的给一个对象添加一些额外的职责。ExecutorCallbackCall对传入的OkHttpCall对象进行了装饰,其中通过OkHttpCall的enqueue方法进行网络请求后,请求来的数据通过CallbackExecutor回调到主线程。
- 适配器模式:适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使本因接口不匹配而无法在一起的两个类能够在一起工作。Retrofit中通过CallAdapter对java8等进行了支持。
- 策略模式:策略模式里面定义了一系列算法,并将每一个算法封装起来,而且使他们可以互相替换。ServiceMethod的invoke方法调用了它的实现类的adapt方法,也就是多态调用ServiceMethod的三个最终实现类的adapt方法
Retrofit是什么?
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。
网络请求的本质仍旧是OkHttp完成的,retrofit只是帮使用者来进行工作简化的,比如配置网络,处理数据等
Retrofit 对Okhttp做了什么
Retrofit主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理
等等。
Retrofit封装了具体的请求,线程切换以及数据转换。
网上一般都推荐RxJava+Retrofit+OkHttp框架,Retrofit负责请求的数据和请求的结果,使用接口的方式呈现,
OkHttp负责请求的过程,RxJava负责异步,各种线程之间的切换,用起来非常便利。
Retrofit的构建过程
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 代码1
//默认只支持okhttp请求,不支持 httpurlconnection 和 httpclient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 代码2
// 添加一个线程管理 Executor,okhttp 切换线程需要手动操作,但是retrofit
// 不需要,就是因为这个Executor 的存在,其实他是handler
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//代码3
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>
(this.converterFactories);
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
代码3
设置默认CallAdapterFactory
在此添加的CallAdapterFactory属于系统默认的,当然,我们可以添加RxJavaCallAdapterFactory。默认的
CallAdapterFactory是 ExecutorCallAdapterFactory 类的对象,在Platform.java Class里面可以梳理出来
defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
所以构建的Retrofit都是用于进行后面请求的需要的内容的一个准备工作。也就是封装Okhttp需要的准备工作。
Retrofit.create()构建 IxxxService 对象的过程
ISharedListService sharedListService = retrofit.create(ISharedListService.class);
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2,1);
上面两行代码需要连起来才能正确的被阅读,因为,在create里面是使用了动态代理的技术方案,而动态代理是运行
时生效的,当我们看到看到create的时候只
create的代码如下:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
// 通过动态代理的方式生成具体的网络请求实体对象
new InvocationHandler() { // 统一处理所有的请求方法
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 根据方法生成一个ServiceMethod对象(内部会将生成的ServiceMethod放入在缓存中,
//如果已经生成过则直接从缓存中获取)
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// 根据ServiceMethod对象和请求参数生成一个OkHttpCall对象,这个OkHttpCall能够
//调用OkHttp的接口发起网络请求
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 调用serviceMethod的callAdapter的adapt方法,并传入okHttpCall,返回一个对象,
//这个的目的主要是为了适配返回类型,其内部会对OkhttpCall对象进行包装
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
动态代理
动态代理的原理主要是在运行时动态生成代理类,然后根据代理类生成一个代理对象,在这个代理对象的方法中中又会调用InvocationHandler的invoke来转发对方法的处理。
那么大家一定要关注一个细节,我们在使用retrofit的时候,对每一个网络请求的产生都必须要先调用create函数,也就是意味着,我们的请求都是通过代理类来进行处理的。但是代理类具体的代理行为是发生在哪里呢?很显然,他并不是在create函数执行的时候,而是在使用具体的接口创建具体网络请求Call的时候,当调用具体网络请求Call的代码示例如下:
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2,1);
在执行上面的代码的时候,它会走代理设计模式的InvocationHandler里面的invoke()函数,也就是所有的网络请求
在创建具体网络请求Call的时候,都会走Invoke,从而我们可以在invoke里面进行各种行为的统一处理,比如:接口的统一配置,也就是注解的解读和网络请求参数的拼接。
ServiceMethod的loadServiceMethod方法
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
// 为什么会缓存?为了效率
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod首先会从缓存中获取ServiceMethod对象,如果没有,则通过Method和Retrofit对象构造一个ServiceMethod对象,并将其放入缓存中。
[一个细节]
每一个method 都有一个自己的ServiceMethod,这就意味着ServiceMethod是属于函数的,而不是类的。也就是我们定义的网络访问接口类,在接口类里面的每一个函数都会在反射阶段形成自己的serviceMethod。那么ServiceMethod是什么呢?
ServiceMethod其实是用来存储一次网络请求的基本信息的,比如Host、URL、请求方法等,同时ServiceMethod还会存储用来适配OkHttpCall对象的CallAdpater。
ServiceMethod的build方法会解读传入的Method,首先ServiceMethod会在CallAdpaterFactory列表中寻找合适的CallAdapter来包装OkHttpCall对象,这一步主要是根据Method的返回参数来匹配的,比如如果方法的返回参数是Call对象,那么ServiceMethod就会使用默认的CallAdpaterFactory来生成CallAdpater,而如果返回对象是RxJava的Obserable对象,则会使RxJavaCallAdapterFactory提供的CallAdpater。然后build方法会解读Method的注解,来获得注解上配置的网络请求信息,比如请求方法、URL、Header等
okHttpCall
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
我们知道,ServiceMethod封装了网络请求的基本信息,比如Host、URL等,我们根据ServiceMethod和请求参数
args就可以确定本次网络请求的所有信息了,OkHttpCall主要是将这些信息封装起来,并调用OkHttp的接口去发送
网络请求,这里,我们就将OkHttpCall看成是一个处理网络请求的类即可。
callAdapter
在retrofit中,invoke() 里面的最后一行代码,
return serviceMethod.callAdapter.adapt(okHttpCall);
那么我们可以设想一下为什么Retrofit还要设计一个CallAdapter接口呢?
先来说一个客观事实,Retrofit真正使用Okhttp进行网络请求的就是OkHttpCall这个类
曾提到了Call对象的创建是通过是通过ServiceMethod.adapt()完成的,这里在看看该方法的源码:
ServiceMethod.adapt()方法:
T adapt(Call<R> call) {
return callAdapter.adapt(call);
}
通过上述源码可知,最终Call对象是调用CallAdapter.adapt(Call)方法创建的,那么CallAdapter及具体的Call对象又是如何生成的呢?如果没有这个适配器模式,会出现什么情况?
很明显,没有适配器的时候此时我们网络请求的返回接口只能直接返回OkHttpCall,那么所有的网络请求都是用okhttpCall进行,这样的话就失去了retrofit 封装网络请求call的意义了,譬如:rxjavaCallAdapterFactory 就没有办法支持。
如果我们想要返回的不是Call呢?比如RxJava的Observable,这种情况下该 怎么办呢?
适配器模式在此发挥了其应用的作用!!!将网络请求的核心类OkHttpCall进行适配,你需要什么类型的数据就通过适配器适配,返回适配后的对象就是了。
正是这种CallApdate接口的设计,使得我们在使用Retrofit的时候可以自定义我们想要的返回类型。此接口的设计也为RxJava的扩展使用做了很好的基础!!!