OKHttp源码解析
整体结构图
整体的流程图
五大拦截器
RetryAndFollowUpInterceptor拦截器
RetryAndFollowupInterceptor是重试重定向拦截器,它的主要作用是负责失败重连。OkHttp中的重定向功能是默认开启的。
总结RetryAndFollowupInterceptor拦截器:
- 创建StreamAllocation对象。
- 调用RealInterceptorChain.proceed()进行网络请求。
- 根据异常结果或响应结果判断是否进行重新请求。
- 调用下一个拦截器,对Response进行处理,返回给上一个拦截器。
BridgeInterceptor拦截器
BridgeInterceptor是桥接和适配拦截器,它的作用是设置内容长度、编码方式以及压缩等等一系列操作,主要是添加头部的作用。
总结BridgeInterceptor拦截器:
- 负责将用户构建的一个Request请求转化为能够进行网络访问的请求,通过给头部添加信息。
- 将这个符合网络请求的Request进行网络请求。
- 将网络请求回来的响应Response转化为用户可用的Response。
CacheInterceptor拦截器
(一)原理和注意事项:
1、原理
- okhttp的网络缓存是基于http协议.
- 使用DiskLruCache的缓存策略.
2、注意事项:
1、目前只支持GET,其他请求方式需要自己实现。
2、需要服务器配合,通过head设置相关头来控制缓存
3、创建OkHttpClient时候需要配置Cache
(二)流程
- 如果配置了缓存,则从缓存中取出(可能为null)
- 获取缓存的策略
- 监测缓存
- 如果禁止使用网络(比如飞行模式),且缓存无效,直接返回
- 如果缓存有效,使用缓存,不使用网络
- 如果缓存无效,执行下一个拦截器
- 本地有缓存、根据条件判断是使用缓存还是使用网络的response
- 把response缓存到本地
ConnectInterceptor拦截器
ConnectInterceptor是网络连接拦截器,我们知道在OkHttp当中真正的网络请求都是通过拦截器链来实现的,通过依次执行这个拦截器链上不同功能的拦截器来完成数据的响应,ConnectInterceptor的作用就是打开与服务器之间的连接,正式开启OkHttp的网络请求。
总结:
首先ConnectInterceptor拦截器从拦截器链获取到前面传递过来的StreamAllocation,接着执行StreamAllocation的newStream方法创建HttpCodec,HttpCodec对象是用于处理我们的Request和Response。
最后将刚才创建的用于网络IO的RealConnection对象,以及对于服务器交互最为关键的HttpCodec等对象传递给后面的拦截器。
CallServerInterceptor拦截器
CallServerInterceptor拦截器主要作用是负责向服务器发起真正的网络请求,并获取返回结果。
CallServerInterceptor的intercept方法:
1 | @Override public Response intercept(Chain chain) throws IOException { |
intercept方法中先是获取五个对象,下面分别介绍这5个对象的含义。
- RealInterceptorChain:拦截器链,真正进行请求的地方。
- HttpCodec:在OkHttp中,它把所有的流对象都封装成了HttpCodec这个类,作用是编码Request,解码Response。
- StreamAllocation:建立HTTP连接所需要的网络组件。
- RealConnection:服务器与客户端的具体连接。
- Request:网络请求。
核心性能优化:okio
(一)、javaNIO和阻塞I/O:
- 阻塞I/O通信模式:调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时才返回。
- NIO通信模式:在JDK1.4开始,是一种非阻塞I/O,在Java NIO的服务端由一个专门的线程来处理所有I/O事件,并负责分发;线程之间通讯通过wait和notify等方式。
(二)、okio
okio是由square公司开发的,它补充了java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理你的数据。OKHttp底层也是用该库作为支持。而且okio使用起来很简单,减少了很多io操作的基本代码,并且对内存和CPU使用做了优化,他的主要功能封装在ByteString和Buffer这两个类中。
okio的作者认为,javad的JDK对字节流和字符流进行分开定义这一世情,并不是那么优雅,所以在okio并不进行这样划分。具体的做法就是把比特数据都交给Buffer管理,然后Buff实现BufferedSource和BufferedSink这两个接口,最后通过调用buffer相应的方法对数据进行编码。
读写操作这块的类图如下:
总体相关类结构如下:
大体流程如下:
第一步,首先是调用okio的source(InputStream in)方法获取Source对象
第二步,调用okio的buffer(Source source)方法获取BufferedSource对象
第三步,调用BufferedSource的readUtf8()方法读取String对象
第四步,关闭BufferedSource
读写总体流程:
1.构建缓冲池,缓冲源对象
2.读写操作
3.关闭缓冲池
okio的优雅之处
类图:
读写流程图:
在说okio的设计模式之前,先说下okio这这个类,该类是一个大工厂,为我们创建出各种各样的Sink、Source 对象,提供了三种数据源InputStream/OutputStream、Socket、File,我们可以把本该对这个三类数据源的IO操作通过okio库来实现,更方便,更高效。
okio高效方便之处
1、它对数据进行了分块处理(Segment),这样在大数据IO的时候可以以块为单位进行IO,这可以提高IO的吞吐率
2、它对这些数据块使用链表来进行管理,这可以仅通过移动指针就进行数据的管理,而不用真正的处理数据,而且对扩容来说十分方便.
3、闲置的块进行管理,通过一个块池(SegmentPool)的管理,避免系统GC和申请byte时的zero-fill。其他的还有一些小细节上的优化,比如如果你把一个UTF-8的String转化为ByteString,ByteString会保留一份对原来String的引用,这样当你下次需要decode这个String时,程序通过保留的引用直接返回对应的String,从而避免了转码过程。
4、他为所有的Source、Sink提供了超时操作,这是在Java原生IO操作是没有的。
5、okio它对数据的读写都进行了封装,调用者可以十分方便的进行各种值(Stringg,short,int,hex,utf-8,base64等)的转化。