OKHttp源码解析

OKHttp源码解析

整体结构图

整体结构图

整体的流程图

流程图

五大拦截器

RetryAndFollowUpInterceptor拦截器

RetryAndFollowupInterceptor是重试重定向拦截器,它的主要作用是负责失败重连。OkHttp中的重定向功能是默认开启的。

总结RetryAndFollowupInterceptor拦截器:

  • 创建StreamAllocation对象。
  • 调用RealInterceptorChain.proceed()进行网络请求。
  • 根据异常结果或响应结果判断是否进行重新请求。
  • 调用下一个拦截器,对Response进行处理,返回给上一个拦截器。

BridgeInterceptor拦截器

BridgeInterceptor是桥接和适配拦截器,它的作用是设置内容长度、编码方式以及压缩等等一系列操作,主要是添加头部的作用。

总结BridgeInterceptor拦截器:

  • 负责将用户构建的一个Request请求转化为能够进行网络访问的请求,通过给头部添加信息。
  • 将这个符合网络请求的Request进行网络请求。
  • 将网络请求回来的响应Response转化为用户可用的Response。

CacheInterceptor拦截器

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
2
3
4
5
6
7
8
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
...
}

intercept方法中先是获取五个对象,下面分别介绍这5个对象的含义。

  • RealInterceptorChain:拦截器链,真正进行请求的地方。
  • HttpCodec:在OkHttp中,它把所有的流对象都封装成了HttpCodec这个类,作用是编码Request,解码Response。
  • StreamAllocation:建立HTTP连接所需要的网络组件。
  • RealConnection:服务器与客户端的具体连接。
  • Request:网络请求。

核心性能优化:okio

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的设计模式之前,先说下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等)的转化。