调用方式
同步调用
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
异步调用
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
总体架构
接口层
OKHttpClient 全局维护一个实例,对全局OkHttp 进行设置
Call 表示一次请求执行,分为同步的ReallCall及AsyncCall
协议层
处理特定的协议逻辑,OkHttp支持Http1/Http2/WebSocket协议
连接层
管理网络连接,网络连接请求,服务返回
- RealConnection 描述一个Socket的连接请求。ConnectionPool 缓存了多个Connection。
- StreamAllocation表示一次全新的网络请求(一个Call请求可能包含有多个StreamAllocation ,比如重定向)
缓存层
管理本地缓存,当本地有可用缓存时,直接采用本地的以节约流量
I/O层
表示数据的实际读写,采用OKIO
Iterceptor 拦截器层
拦截器逻辑
首先由getResponseWithInterceptorChain 把系统及自定义的拦截器传入
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
调用 RealInterceptorChain 的process 方法轮训每个拦截器
public final class RealInterceptorChain implements Interceptor.Chain {
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
......
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
......
return response;
}
}
RetryAndFollowUpInterceptor
- 访问失败后进行重试
- 对服务器要求重定向的情况下进行连接复用
BridgeIterceptor
- 设置内容长度及类型
- 设置cookie
- 设置gzip并在获取response 后进行释放
- 设置其他报头
CacheInterceptor
- 增加符合网络要求的cache
- 更新服务器的最新cache
- 当前cache失效则输出cache
ConnectInterceptor
CallServerInterceptor
整体流程
任务队列
同步
直接执行
异步队列
client.newCall(request).enqueue();
// 具体执行
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加正在运行的请求
runningAsyncCalls.add(call);
//线程池执行请求
executorService().execute(call);
} else {
//添加到缓存队列排队等待
readyAsyncCalls.add(call);
}
}
- 若线程数未达到最大请求则通过线程池直接执行
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
- 若线程数达到最大请求则加入等待队列,当其他线程执行完成后调用client.dispatcher().finished(this); 来继续执行等待的队列
缓存
Http 缓存策略
- Expires 超时时间,当访问服务器时比较超时时间,若未超过直接返回缓存结果
- Cache-Control 表示当前资源的有效期
- 条件GET请求
客户端第一次请求时,服务器返回:
Last-Modified: Tue, 12 Jan 2016 09:31:27 GMT
当客户端二次请求时,可以头部加上如下header:
If-Modified-Since: Tue, 12 Jan 2016 09:31:27 GMT
如果当前资源没有被二次修改,服务器返回304告知客户端直接复用本地缓存。
- Etag
ETag是对资源文件的一种摘要,可以通过ETag值来判断文件是否有修改。当客户端第一次请求某资源时,服务器返回:
ETag: "5694c7ef-24dc"
客户端再次请求时,可在头部加上如下域:
If-None-Match: "5694c7ef-24dc"
如果文件并未改变,则服务器返回304告知客户端可以复用本地缓存。