Okhttp整体流程图
配置中心OkHttpClient
OkHttpClient是整个okhttp的大管家,通过它内部的参数,可以自定义各种请求配置
- Dispatcher
线程管理工具,内部使用ExecutorService实现,使用异步请求时,会把请求放入队列中,ExecutorService来控制什么时候执行
- Proxy
配置代理,是一个枚举,有DIRECT\ HTTP\ SOCKS三个值可以选择
- Protocol
一个List列表,即可以配置支持的Http协议版本
- ConnectSpec
一个List列表,配置支持的TCP协议和SSL/TLS协议版本
- SocketFactory
建立TCP连接的工厂实现类
- SSLSocketFactory
建立SSL连接的工厂实现类
- CertificateChainCleaner
建立SSL链接的时候,清除无用证书链,只保留直接的访问的地址的证书
- HostnameVerifier
建立SSL链接时,验证证书的有效性
- CertificationPinner
固定证书,通过这个类,可以设置证书的公钥,以及签发这个证书的CA机构及其父类机构的公钥,主要是为了防止伪造CA机构攻击
- Authenticator
权限验证失败后的回调接口,只有在服务器状态码为401时会回到该接口,可以在这里面实现token的刷新操作
- ConnectPool
管理http连接的重用,减少网络的延迟
- Dns
域名解析成IP地址的工具,默认使用java提供的DNS解析器
连接池ConnectPool
连接池复用的其实是Socket,也就是TCP连接,基于http1.1的keep-alive机制,一个TCP连接在请求结束后并不会关闭,可以被相同的请求重复使用。
复用连接,大大提高了性能,不用每次请求都去建立物理连接
StreamAllocation
用于协调请求(Call),数据流(Stream),物理连接(RealConnect)。每次请求都会创建一个StreamAllocation,因为连接池复用的关系,存在多个StreamAllocation对应同一个物理连接(RealConnect)的情况。
RealConnect
RealConnect是物理连接的封装,包含了Socket链接(TCP链接),TLS链接。它有一个allocations变量,表示建立该连接上的StreamAllocation的个数,如果allocations长度为0,表示该连接是空闲的,否则表示该连接在被使用。
ConnectPool
连接复用的管理类,复用的就是RealConnect。说到复用,肯定少不了对象的存储,获取和清理机制。
ConnectPool用一个双端队列ArrayQueue来存储RealConnect,每创建一个新的链接都会放进连接池中,然后尝试启动清理线程
尝试取出连接的时候,会遍历整个队列,找到合适的链接,然后把RealConnect的allocations计数器加1
ConnectPool的清理机制,允许的最大空闲连接数是5个,每个连接允许的最长空闲时间是5分钟,根据RealConnect中的allocations列表的长度,即建立在此连接上的StreamAllocation的个数是否为0,确定是否为空闲状态
是由一个异步任务实现的,启动的时间是第一次调用put方法后。首先开启while(true)循环,调用cleanup尝试清理,返回下一次清理的等待时间,然后调用wait(time)方法等待后,执行下一次清理,当队列为空时,退出循环结束清理
清理的条件是 1.如果某个连接的空闲时间超过5分钟或者空闲连接数超过5个,则清理空闲时间最长的那一个 2.如果都不满足,则等待极限时间和最长空闲时间的那个连接的差值,然后继续清理 3.如果所有的连接都在使用,则等待5分钟,继续清理
OKHttp缓存
http定义了如下的Header来控制缓存
Expires过期时间Cache-Control
这2个都是服务端在response中的header,表示资源的过期时间,客户端再次请求相同资源的时候,先验证是否过去,没有过期时直接从返回缓存结果,否则重新请求,注意Cache-Control的优先级比Expires要高
last-modifiedlast-modified-since
last-modified服务端返回表示资源上一次修改时间,客户端请求相同资源时用last-modified-since这个header带上服务端返回的资源上一次修改时间,如果资源没有被修改,服务端返回304告知客户端直接复用缓存
ETageif-none-match
ETage是服务端返回表示资源上一次请求的摘要,客户端请求相同资源的时候,使用if-none-match发送上一次资源的摘要,服务端对比摘是否改变,如果没有,返回304告知客户端直接使用缓存
OKhttp的缓存是由CacheInterceptor来完成的,根据不同的缓存策略,决定走网络还是直接从缓存中拿出响应,而缓存的基础就是http定义的缓存控制域
Interceptor拦截器
每次请求(直接复用缓存的除外)最终都会调用到RealCall的getResponseWithInterceptorChain()方法,在这个方法中,会把
自定义interceptor开发者自定义,可以对request和respons做各种定制操作``RetryAndFollowUpInterceptor重试和重定向,创建StreamAllocation,添加到chain中``BriageInterceptor桥接应用层和实际网络层,即把应用层request包装成网络可用的request,把网络返回的Response包装成应用层可用的ResponseCacheInterceptor缓存控制,使用缓存策略决定使用缓存还是重新请求网络ConnectInterceptor使用socket建立Socket连接,如果是https请求还是建立TLS连接自定义NetworkInterceptor开发者自定义,主要是做网络调试时使用CallServerInterceptor跟服务器做数据调用,发送并接受数据
按照顺序加入到interceptors列表这个变量中,然后使用interceptors和index(此时index=0)作为参数创建一个RealInterceptorChain拦截器链
然后调用chain.process(),该方法中会从interceptors列表取出第index个interceptor,再创建一个新的RealInterceptorChain拦截器链(index=index+1),然后执行interceptor.intercep(chain)方法,把新的拦截器链作为参数传入,在interceptor的interceptor()方法中,在做完前置工作后,都会调用chain.process()来获取Response,如此循环,每一个interceptor都被链接了起来
如果当前interceptor能够返回Response,则直接返回。如果不能,则在intercep()方法中,对request做各种前置工作,然后调用chain.process(),这个方法主要有2个作用
- 可以给
chain中的变量赋值,提供给后续的interceptor使用 - 尝试从下一个
interceptor中获取response
直到最后一个CallServerInterceptor返回Response后,层层向上传递,每一个interceptor都可以对response做各种收尾工作
okhttp的优势
okhttp是一个非常底层的网络库,实现了应用层协议到建立TCP连接整个过程,并且对外提供了各种配置项,几乎可以应对绝大多数的网络需求。读了okhttp的源码之后,最大的感受就是okhttp强大的可配置和高扩展性,这也是我觉得它的最大优势。
高扩展性
通过protocol可以配置http的版本,并且天然支持http2,http2相比于http1.1新增了多路复用、数据流、报文头压缩的功能,在性能上有了巨大提升,所以,okttp的高性能有一部分来自于http2的支持
通过ConnectSpec可以配置SSL/TLS连接的加密套件的版本,以及加密方式
针对安全性要求很高的场景,可以通过HostnameVerifier自定义证书的验证方式,还可以使用CertificationPinner设置固定的CA公钥,来防止CA篡改攻击
如果有特殊的域名解析规则,可以通过Dns配置自己的域名解析服务
高扩展性的另一个提现就是okhttp的拦截器机制,可以让开发者很方便配置自己的拦截器,方便在数据请求前后各种操作,比如添加Header,打印日志,对resposne做拦截等等都能得心应手
强大的性能
1.因为支持http2,性能相比于http1.1有很大的提升
2.复用连接,每次建立连接后会放入连接池中,下一次相同请求的时候先从看连接池是否有可用的连接,如果有就直接复用,这样不用每次请求都去建立TCP链接,提高了效率。
3.使用okio,okio是对原生Java IO/NIO的封装和优化,其中的缓冲区的设计提高了写数据的性能