流程图
同步请求
异步请求
- 判断当前 call 是否只执行了一次,否则抛出异常
- 创建一个 AsyncCall 对象,它其实是一个 Runable 对象
- 通过 client.dispatcher().enqueue() 传入 AsyncCall 对象执行异步请求,如果当前运行的异步任务队列(runningAsyncCalls)元素个数小于
maxRequests 并且当前请求的 Host 个数小于 maxRequestsPerHost 则直接放进运行对象并执行当前的任务,否则放进准备队列中 (readyAsyncCalls) - 如果第 3 步可以执行,则会调用 AsyncCall 的 execute 方法,之后调用 getResponseWithInterceptorChain() 获取 Response,执行
onResponse 或 onFailure。这也 印证了上面提到的异步任务回调是在子线程中 - execute 方法末尾会必调 client.dispatcher().finished(this) 方法
- finished 方法中会做三件事:
- 从 runningAsyncCalls 中清除当前任务
- 通过 promoteCalls方法调整整个异步任务队列,主要工作是判断准备队列 readyAsyncCalls 中的任务是否可以执行
- 重新计算正在执行的任务数量,为 0 则执行 idleCallback
拦截器
拦截器可以添加、移除或者替换请求的头信息,也可以改变传输的主体部分。
应用拦截器
发送前进行拦截
网络拦截器
对响应的数据进行拦截
内部拦截器
RetryAndFollowUpInterceptor
重定向拦截器:客户端向服务器发送一个请求,获取对应的资源,服务器收到请求后,发现请求的这个资源实际放在另一个位置,于是服务器在返回的响应头的
Location字段中写入那个请求资源的正确的URL,并设置reponse的状态码为30x 。
工作流程
- 创建一个 StreamAllocation。
- 调用下一个chain 。
- 判断是否重定向。重定向会对 request做一些修改,返回新的request,否则会返回null。
- followUp == null 无重定向,释放资源,直接返回response。
- 比较重定向前后的 host、port、scheme是否一致,一致的话复用, 否则重新创建 StreamAllocation。
- 通过 while (true) ,重复步骤
BridgeInterceptor
桥接拦截器:负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应。
工作流程
发起请求前:
- 如果这个请求有请求体,就添加 Content-Type, Content-Length等。
- 如果这个请求没有 Host,就通过 url 来获取 Host 值添加到 Header中。
- 如果这个请求没有接收的数据类型 Accept-Encoding,且没指定接收的数据范围,就添加默认接受格式为 gzip。
- 去 CookieJar 中 根据 url 查询 Cookie 添加到 Header。
- 如果当前没有,就添加 User-Agent 信息。
发起请求后:
- 解析响应 Header 中的 Cookie
- 如果想要数据的格式是 gzip,就创建 GzipSource 进行解压, 同时移除 Content-Encoding 和 Content-Length
CacheInterceptor
缓存拦截器:最快的请求就是不请求,直接用缓存。
- 根据Request和之前缓存的Response得到CacheStrategy
- 根据 CacheStrategy决定是请求网络还是直接返回缓存
- 如果 step 2中决定请求网络,则在这一步将返回的网络响应和本地缓存对比,对本地缓存进行改增删操作
ConnectInterceptor
连接拦截器:部维护了可以重复使用的 Socket 连接池,减少握手次数,加快请求响应
- 连接 RealConnection 是对 Socket 的封装。
- OkHttp 的连接复用主要是通过 StreamAllocation 来实现的,每个连接上持有一个。
- StreamAllocation 引用的列表,以此来标识当前连接是否空闲。它里面维护了List的引用。List中StreamAllocation的数量也就是socket被引用的计数,
如果计数为0的话,说明此连接没有被使用就是空闲的,需要通过下文的 算法实现回收;如果计数不为0,则表示上层代码仍然引用,就不需要关闭连接。 - 判断连接是否可以重用,除了比较连接当前的 host,也可以比较路由信息。
- 连接池在添加新连接时会运行清理任务,默认最多空闲连接为5,最长空闲时间为5分钟。
CallServerInterceptor
读写拦截器:它负责实现网络 IO,所有拦截器都要依赖它才能拿到响应数据。
- CallServerInterceptor 首先会将请求中的 header 写给服务器。
- 如果有请求体的话,会再将 body 发送给服务器。
- 最后通过httpCodec.finishRequest() 结束 http 请求的发送。
- 然后从连接中读取服务器的响应,并构造 Response。
- 如果请求的 header或服务器 响应的 header 中,Connection 的值为 close,就关闭连接。
- 最后返回 Response。
Get到的知识点
- 责任链模式、建造者模式的运用
- 线程池的运用