OkHttp开发者之一介绍OkHttp的文章里面,作者说到:
the whole thing is just stack of built-in interceptors—所有的东西都交给拦截器完成
Interceptor是okhttp最和谐的一个东西,不要误以为他只负责请求一些额外的处理,实际上他把实际的网络请求,缓存,透明压缩等功能都统一起来,每一个功能都是一个Interceptor,他们在process ->intercept->process->intercept…使用责任链模式,层层相连,最终完成了一个完整的网络请求
1.在配置OkHttpClient 时设置interceptors;
2.负责失败重试以及重定向的RetryAndFollwUpInterceptor;
3.负责把用户构造的请求转换成发送到服务器的请求、把服务器返回的响应转换成用户友好的响应的BridgeIntercepter;
4.负责读取缓存直接返回、更新缓存的CacheInterceptor
5.负责和服务器建立连接的ConnectInterceptor;
6.配置网络networkInterceptors;
7.负责向服务器发送请求数据、从服务器读取响应数据CallServcerInterceptor
责任链模式
他包含了一些命令对象和一系列的处理对象,每一个处理对象决定他能处理那些命令对象,他也知道如何将他不能处理的命令对象传递给该链的下一个处理对象.该模式还描述了往该处理链的末端添加新的处理对象的方法.
对于把request编程response这件事来说,每个interceptor都可能完成这件事情,所有我们遵循链条让每个Interceptor自行决定能否完成(自行完成或者交给别人).如此,完成网络请求这件事件就彻底从RealCall类中剥离出来,简化了各自的逻辑和责任.
其实这个和View的点击事件类似
以下ConnectInterceptor 和CallServerInterceptor,他们两个是和服务器的实际通信的
一.默认的拦截器
拦截器名称 | 用处 |
---|---|
retryAndFollowUpInterceptor | 失败和重定向拦截器 |
BridgeInterceptor | 封装request和response过滤器 |
CacheInterceptor | 缓存相关的过滤器 |
ConnectInterceptor | 负责和服务器建立连接,连接池等 |
networkInterceptors | 配置 OkHttpClient 时设置的 networkInterceptors |
CallServerInterceptor | 负责向服务器发送请求数据、从服务器读取响应数据(实际网络请求) |
接下来,我们挑选合适的Interceptor 进行研究,体会OkHttp的优雅
二. ConnectInterceptor :建立连接
ConnectInterceptor#intercept(Chain chain)
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
建立连接就是创建了一个HttpCodec 对象;
HttpCodec有连个实现:Http1Codec和http2Codec,顾明思意,他们分别对应Http/1.1和2版本的实现
创建HtpCodec对象的过程中,找到一个可用的RealConnection,输入BufferedSource和BuffereSink创建httpCodec对象,供后续步骤使用
二. CallServerInterceptor :发送和接收数据
@Override public Response intercept(Chain chain) throws IOException {
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
httpCodec.finishRequest();
Response response = httpCodec.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
// 省略部分检查代码
return response;
}
主要作用:
- 向服务器发送 request header;
- 如果有 request body,就向服务器发送;
- 读取 response header,先构造一个 Response 对象;
- 如果有 response body,就在 3 的基础上加上 body 构造一个新的 Response 对象;
三.发送异步网络请求
在enqueue 的源码中:把CallBack进行封装在runnable里面;然后使用dispatcer进行调度;如果当前还能执行并发,就执行;否则加入readyAsynCalls队列;而正在执行的请求执行完毕后,会调用promoteCalls函数,来吧readyasynCall队列中的AsyncCall放在runningAsycCalls中
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
// RealCall#enqueue
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
// Dispatcher#enqueue
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
四.返回数据的获取
在执行同步Call#execute() 和异步CallBack#onResponse()请求完成之后,我们获得了Response对象: 里面我们获得各种参数;其中body最为特殊,因为服务器返回的数据可能非常大,所有必须通过数据流的方式进行访问;
Response中body的内容 | 说明 |
---|---|
body.string() | String |
body.bytes() | byte[] |
body.byteStream | inputStream |
1.每个body只能被消费一个,多次消费会抛异常
2.body必须被关闭,否则发送资源泄漏
五.Http 缓存
在建立连接和服务器通信之前,就是CacheInterceptor,在建立连接之前,我们检查响应是否已经缓存是否可用,如果是则直接返回缓存的数据,否则就进行后面的流程,并在返回之前,把网络的数据希尔缓存;
六.总结
回顾流程:
- OkHttpClient 实现了Call.Factory,负责为Request创建Call;
- RealCall是Call的实现,使用enqueue异步接口通过Dispatcher利用ExecutorService实现,最终进行网络请求时和同步execute()接口一致,都是通过getResponseWithInterceptorChain()函数实现
- getResponseWithInterceptorChain()中利用InterceptorChain,分层实现缓存,透明压缩,网络Io等功能