OkHttp
基础用法
okhttp的用法很简单,先创建一个OkHttpClient构造方法创建一个OkHttpClient对象,然后再通过Request的Builder方法创建一个Request对象,然后就调用client的newCall方法传入request对象,创建一个Call,就可以通过这个Call来进行同步或者异步网络请求了。
通过Http进行WebScoket请求
首先是创建OkHttp的Client,只不过创建Client的时候需要配置下读的超时时间、写的超时时间以及连接的超时时间。在正常的构建Request,以及一个WebsocketListener回调。然后调用okHttp的newWebSocket方法传入request和websocketListener,之后调用okHttpClient.dispatcher.executorService.shutdown去执行。
其中我们在WebSocketListener回调中需要设置下面几个方法:
- onOpen:建立连接时调用,在此方法中发送消息
- onMessage:收到消息时调用此方法
- onClosed:链接关闭成功时调用
- onClosing:连接正在关闭时调用
- onFailure:期间出错时调用
为什么只用一个OkHttpClient
OkHttpClient是okhttp框架的客户端,用户使用okhttp进行各种设置,发起各种网络请求都是通过OkHttpClient完成的。每个OkHttpClient内部都维护了属于自己的任务队列,连接池,Cache,拦截器等,所以应该全局共享一个OkHttpClient实例,是应该不重复创建连接池和线程池。而且单例不会影响OkHttpClient的拓展性,,在需要特殊配置一个OKHttpClient的时候,这个局部的OKHttpClient 引用的连接池和线程池是复用自全局单例的就行(使用Client.newBuilder再配置局部属性)
拦截器
流程
经过Dispatcher调度器的调度后,请求就会进入OkHttp的拦截器链中。拦截器链是典型的责任链设计模式。
然后拦截器的调度主要是通过RealInterceptorChain来操作的。
RetryAndFollowUpIntercrptor:重定向拦截器
进入拦截器链后,首先是RetryAndFollowUpInterceptor重定向拦截器。他的主要作用是负责请求的重定向操作,用于处理网络请求失败后的重试。
当发生重定向事件的时候,首先要销毁与原url建立的链接,与新的地址进行一个链接,创建对应的StreamAllocation。
这里的重定向是指client向server发送一个请求,要求获取一个资源,server接收到这个请求后,发现请求的这个资源实际存放在另一个位置,于是server在返回的response header的Location字段中写入那个请求资源的正确的URL,并设置reponse的状态码为30x,client接收到这个response后,发现状态码为重定向的状态码,就会去解析到新的URL,根据新的URL重新发起请求。
重试就是指,如果在请求的过程中,发生了Route异常或者IO异常,那么就会经过死循环进行重试,如果不是这个问题,就不会重试
BridgeInterceptor:桥拦截器
之后会进入BridgeInterceptor桥拦截器,在这块会把传入的请求添加请求头,并根据需求判断是否需要添加Cookies。当有响应之后,会为响应添加对应的响应头,并保存cookies,同时在这一层会判断是否需要gzip压缩。
CacheInterceptor:缓存拦截器
如果在BridgeInterceptor正常,那就会进入下一个拦截器:CacheInterceptor缓存拦截器。在这块主要是缓存。他首先通过将请求的url进行了md5加密后的转为了16进制的字符串作为key,根据这个key,去磁盘中获得这个缓存(OKio还有DiskLruCache获得,LRU算法),当获得缓存后紧接着获得缓存策略,根据缓存中请求头的Cache_Control字段,以及缓存存在的时长,判断缓存是否可用,如果可用,就不去进行网络请求,直接将缓存的Response返回。如果不可以就会继续进行网络请求,还是会下一次的拦截器。然后将的Response进行跟据需要缓存更新,或者存入。这里的存入和取的时候一样,使用url获得的md5值来将Response存入到磁盘中。同时缓存只存入get方法。
ConnectInterceptor:连接拦截器
在之后进入连接拦截器,他会首先获得一个健康的连接,健康的连接是用过下面的过程来获得的:当本地有连接时,直接用,如果没有去连接池查找,找到后,直接用,否则补充一些路由信息后,再次查找。如果还是没有找到,此时他就会去创建一个新的连接。当创建新连接时,它就会建立一个socket连接,并用OKio封装输入输出流。而连接池的是ConnectionPool通过双端队列进行维护。默认的最大空闲连接数为5,在清理回收的时候利用(连接上transmitter可用数量)引用计数法以表明此连接是否是空闲连接。
CallServerInterceptor:请求拦截器
连接拦截器之后就是请求拦截器,这块是真正进行请求的。他会把连接中封装好的Okio,先将请求头写入到连接中,然后会进行100-continue判定,来看服务器是否处理POST请求,如果可以接受或者无100-continue判定,那么他就会继续将请求发送过去。之后就是接受服务器返回的响应头和响应体。
最后就是将结果依次有拦截器链返回,最后将Response返回到要调用OkHttp的地方。
应用拦截器和网络拦截器区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wx5qV8yQ-1615630224114)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0d579132-606c-4d2f-bb06-7aa58f363c2d/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AUykgaZw-1615630224116)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a3d1fe07-26b5-4e18-95e4-746ced30c8b4/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uyxPjJpB-1615630224119)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/34dbc740-816e-418d-969e-535bbb9493e5/Untitled.png)]
Application interceptors
- 不需要关心是否重定向或者失败重连
- 应用拦截器只会调用一次,即使数据来源于缓存
- 只考虑应用的初始意图,不去考虑Okhhtp注入的Header比如:if-None-Match,意思就是不管其他外在因素只考虑最终的返回结果
- 根据第三张图我们可以看出,自定义的应用拦截器是第一个开始执行的拦截器,所以这句话的意思就是,应用拦截器可以决定是否执行其他的拦截器,通过Chain.proceed().
- 和上一句的意思差不多,可以执行多次调用其他拦截器,通过Chain.proceed().
Network Interceptors
- 根据第二张图,我们可以理解这句话的意思是,网络拦截器可以操作重定向和失败重连的返回值
- 根据第一张图,我们可以以看出,这句换的意思是,取缓存中的数据就不会去还行Chain.proceed().所以就不能执行网络拦截器
- 意思是通过网络拦截器可以观察到所有通过网络传输的数据
- 根据第三张图我们可以看出,请求服务连接的拦截器先于网络拦截器执行,所以在进行网络拦截器执行时,就可以看到Request中服务器请求连接信息,因为应用拦截器是获取不到对应的连接信息的。
-
请求流程
在构造OkHttp请求流程中,核心就在那个newCall方法,newCall方法的作用就是根据传入的request创建了一个call。而这块newCall其实是Call接口唯一的实现类RealCall实现的。newCall就调用了RealCall的newRealCall方法。
所以也就是说我们的同步异步方法都是RealCall在处理。而调用RealCall的同步或者异步请求方法最后都会进入dispatcher方法进行调度。
Dispatcher维护了三个队列,分别是准备运行的异步队列、正在运行的异步队列以及正在运行的同步队列。
如果是同步请求就直接加入正在运行的同步队列中,直接调用getResponseWithInterceptorChain()。
如果是异步请求,先放到就绪队列中去,然后再判断是否达到同时最大请求数64或者同一host最大请求数5,如果满足其中一个条件时,就把请求放入正在等待的异步队列,否则放入正在运行的异步队列。每当一个请求完成后,就通过try catch执行finish方法,在这个方法中就把ReadyAsyncCalls中满足条件的请求放入RunningAsyncCalls。然后再通过线程池去执行getResponseWithInterceptorChain()。
OkHttp缓存
再详细说说CacheInterceptor的流程:
- 如果此次网络请求有缓存数据,则通过请求的URL从DiskLruCache中获取响应缓存,并根据请求中的Cache-Control字段来获取缓存策略。
- 如果根据缓存策略不使用网络请求(例如Cache-Control的值为only-if-cached,仅用缓存 ),并且没有缓存,返回504错误。
- 根据缓存策略不使用网络请求,则直接使用缓存。
- (走到这,说明网络请求可用。)进入ConnectInterceptor进行网络请求。如果响应码是304 Not Modified,则缓存可用(对比缓存),更新缓存后直接返回。
- 否则读取返回的结果并进行缓存。
用到的设计模式
- 建造者模式
这从它的基本使用就可以看出来:
- 工厂模式
一个是OkHttpClient类实现的WebSocket.Factory接口,还有一个就是Call接口。
public interface Call extends Cloneable {
...
interface Factory {
Call newCall(Request request);
}
}
- **责任链模式
优点**
1)降低耦合度:客户端不需要知道请求由哪个处理者处理,而处理者也不需要知道处理者之间的传递关系,由系统灵活的组织和分配。
2)良好的扩展性:增加处理者的实现很简单,只需重写处理请求业务逻辑的方法。
缺点
1)请求会从链头发出,直到有处理者响应,在责任链比较长的时候会影响系统性能。
2)请求递归,调试排错比较麻烦。
- 观察者模式
观察者有两个,一个是EventListener,另一个是WebSocketListener。两者都可以看作是生命周期监听器,前者监听请求/响应,后者监听web socket。