转载

Android--Okhttp源码详解

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-modified
last-modified-since

last-modified服务端返回表示资源上一次修改时间,客户端请求相同资源时用last-modified-since这个header带上服务端返回的资源上一次修改时间,如果资源没有被修改,服务端返回304告知客户端直接复用缓存

ETage
if-none-match

ETage是服务端返回表示资源上一次请求的摘要,客户端请求相同资源的时候,使用if-none-match发送上一次资源的摘要,服务端对比摘是否改变,如果没有,返回304告知客户端直接使用缓存

OKhttp的缓存是由CacheInterceptor来完成的,根据不同的缓存策略,决定走网络还是直接从缓存中拿出响应,而缓存的基础就是http定义的缓存控制域

Interceptor拦截器

每次请求(直接复用缓存的除外)最终都会调用到RealCall的getResponseWithInterceptorChain()方法,在这个方法中,会把

自定义interceptor
RetryAndFollowUpInterceptor
BriageInterceptor
CacheInterceptor
ConnectInterceptor
自定义NetworkInterceptor
CallServerInterceptor

按照顺序加入到 interceptors列表 这个变量中,然后使用 interceptorsindex(此时index=0) 作为参数创建一个 RealInterceptorChain 拦截器链

然后调用 chain.process() ,该方法中会从 interceptors列表 取出第 indexinterceptor ,再创建一个新的 RealInterceptorChain 拦截器链( index=index+1 ),然后执行 interceptor.intercep(chain) 方法,把新的拦截器链作为参数传入,在 interceptorinterceptor() 方法中,在做完前置工作后,都会调用 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的封装和优化,其中的缓冲区的设计提高了写数据的性能

原文  https://juejin.im/post/5d751241f265da03c128be71
正文到此结束
Loading...