OkHttp介绍
https://github.com/square/okhttp
An HTTP & HTTP/2 client for Android and Java applications.
OkHttp是支持HTTP、HTTP2的网络请求库。这里解析的是OkHttp 3.11.0版本的代码。
使用方式:
创建client
1
OkHttpClient client = new OkHttpClient();
创建网络请求
1
2
3Request request = new Request.Builder()
.url(ENDPOINT)
.build();发送请求
1
2
3
4
5// 同步方式
Response response = client.newCall(request).execute()
// 异步方式
client.newCall(request).enqueue(callback)
OkHttpClient
1 | public OkHttpClient() { |
OkHttpClient使用Builder模式创建,负责管理全局需要的配置。
Request的创建
Request的创建也使用了Builder模式,负责单个请求的配置信息。
1 | Request(Builder builder) { |
发起请求
client.newCall(request)负责构建一个RealCall,RealCall里存储了一个Request和OkHttpClient,以及请求的事件回调EventListener等信息。
1 | @Override public Call newCall(Request request) { |
RealCall实现了OkHttp3.Call接口,实现了请求的发起、执行、取消、状态判断等。
1 | public interface Call extends Cloneable { |
Dispathcer
无论同步和异步请求均使用到了Dispathcer,负责进行请求的管理,例如取消请求、返回请求和数量等处理。
1 | // 最大请求数 |
对于异步请求队列,Dispatcher会开启一个线程池进行处理。
1 | public synchronized ExecutorService executorService() { |
同步请求
execute()方法用于发送同步请求,等待请求响应返回一个Response。
1 | @Override public Response execute() throws IOException { |
异步请求
enqueue()则是发送异步请求,每个请求是一个AsyncCall,将请求加入队列,在Dispatcher的线程池进行处理。
1 | @Override public void enqueue(Callback responseCallback) { |
AsyncCall持有一个Callback,在execute处理请求,回调给调用方。
1 | @Override protected void execute() { |
getResponseWithInterceptorChain()
无论同步还是异步请求,最终都由getResponseWithInterceptorChain方法返回响应Response。这里用到了责任链模式,通过一系列拦截器对请求进行加工处理。
1 | Response getResponseWithInterceptorChain() throws IOException { |
此处借用Piasy的图:
- 在配置 OkHttpClient 时设置的 interceptors;
- 负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
- 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
- 负责读取缓存直接返回、更新缓存的 CacheInterceptor;
- 负责和服务器建立连接的 ConnectInterceptor;
- 配置 OkHttpClient 时设置的 networkInterceptors;
- 负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
至此OkHttp请求发送和响应解析完毕。
OkHttp拦截器
拦截器的调用顺序:
- AppInterceptor 自定义拦截器
- RetryAndFollowUpInterceptor 重试
- BridgeInterceptor 处理header、cookie和连接封装
- CacheInteceptor 缓存处理
- ConnectInterceptor 建立连接
- NetworkInterceptor 网络拦截器
- CallServerInterceptor 数据交换
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor负责创建StreamAllocation和失败重连。最大重连次数是20次。
1 | private static final int MAX_FOLLOW_UPS = 20; |
具体看看intercept的处理。
1 | // 创建StreamAllocation |
recover负责检测异常。
1 | private boolean recover(IOException e, boolean routeException, Request userRequest) { |
BridgeInterceptor
1 | Response networkResponse = chain.proceed(requestBuilder.build()); |
CacheInteceptor
Http的缓存主要通过Cache-control控制。
强制缓存与对比缓存
强制缓存:
- private: 客户端可以缓存
- public: 客户端和代理服务器都可缓存
- max-age=xxx: 缓存失效时间
- no-cache: 使用对比缓存
- no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发
对比缓存:
- Last-Modified:服务器在响应请求时,告诉浏览器资源的最后修改时间
- If-Modified-Since:通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间
- Etag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识
对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
缓存配置:
1 | // 缓存目录 |
CacheInteceptor会根据缓存策略获取缓存进行处理。
1 | // 查询缓存策略 |
ConnectInterceptor
1 | RealInterceptorChain realChain = (RealInterceptorChain) chain; |
核心是newStream的处理。
1 | // 找到可用连接 |
看看findHealthyConnection内部。
1 | // 开启循环一直寻找可用请求 |
看看findConnection的源码。
1 | private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, |
连接池ConnectionPool
管理http和http/2的链接,以便减少网络请求延迟。同一个address将共享同一个connection。该类实现了复用连接的目标。
1 | public final class ConnectionPool { |
RealConnection
1 | public final class RealConnection extends Http2Connection.Listener implements Connection { |
连接的复用
具体的连接池复用逻辑在ConnectionPool的get方法里:
1 | @Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) { |
1 | public boolean isEligible(Address address, @Nullable Route route) { |
即遵循上述规则的连接可进行复用。
连接的清理
开启一个cleanup线程池持续进行清理。
1 | private final Runnable cleanupRunnable = new Runnable() { |
1 | long cleanup(long now) { |
具体清理的逻辑如下:
- 清理任务遵循最长空闲优先清理规则;
- 清理完毕所有连接,清理任务才结束;
- 下一次put的时候,如果已经停止的清理任务则会被再次触发。
CallServerInterceptor
1 | @Override public Response intercept(Chain chain) throws IOException { |
一句话总结是通过建立好的连接进行数据交换。
AppInterceptor和NetworkInterceptor的区别
AppInterceptor适用于在请求前统一添加一些公共参数,例如在添加自定义Header。或者是用于请求和响应的日志打印。
NetwrokInterceptor在这一层拦截器中可以获取到最终发送请求的request,也可以获取到真正发生网络请求回来的response响应,从而修改对应的请求或者响应数据。
比如业务接口需要自定义AppInterceptor增加Header实现接口鉴权。
1 | public class HeaderInterceptor implements Interceptor { |
参考文章:
https://www.cnblogs.com/chenqf/p/6386163.html
https://www.jianshu.com/p/fe43449682d6
https://www.cnblogs.com/chenqf/p/6386163.html
https://juejin.im/post/5afb89dcf265da0ba26727c7