Okhttp学习笔记(三)核心之二:okHttp拦截器链
学习资料:
上一篇文章:
Okhttp学习笔记(一) 初步认识&& 源码浅析
Okhttp学习笔记(二)核心之一:Dispatcher
拦截器链
前面可以看到,获取的response都是通过 getResponseWithInterceptorChain这个方法获取的,okhttp通过拦截器链,一步步构建返回请求结果。
// AsyncCall 方法
// 异步时执行的execute方法
@Override protected void execute() {
...
try {
Response response = getResponseWithInterceptorChain(); // 调用方法获取结果
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
...
} finally {
client.dispatcher().finished(this);
}
}
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());
}
// 将HTTP请求写入网络IO流,并从IO流中读取服务端返回的数据
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
系统默认的五个拦截器:
retryAndFollowUpInterceptor -> BridgeInterceptor -> CacheInterceptor -> ConnectInterceptor -> CallServerInterceptor;
最后把拦截器的列表传入构建了RealInterceptorChain,并调用了它的proceed方法;
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// 构建下一个拦截器链 注意这里是 index + 1
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
// 将下一个拦截器链传入
// 一层层回调获取response(也是执行了 proceed方法)
Response response = interceptor.intercept(next);
...
}
小结:
1、构建一个拦截器的list,包括用户自定义的和默认的五个拦截器
2、构建RealInterceptorChain,执行它的proceed方法
3、在proceed里构建下一个RealInterceptorChain(index为下一个的坐标),并调用当前interceptor的intercept方法获取结果并返回
小结二:
1、发起请求前对request进行处理
2、调用下一个拦截器,获取response
3、对response进行处理,返回给上一个拦截器
五个内部的拦截器
RetryAndFollowUpInterceptor
- 创建StreamAllocation 对象
- 调用RealInterceptorChain.proceed(…) 进行网络请求
- 根据异常结果或者相应结果判断是否要进行网络请求
- 调用下一个拦截器,对response进行处理,返回给上一个拦截器
StreamAllocation
- 分配Stream(建立执行http请求所需要的网络组件)
- 获取连接服务端的connection,获取和服务端进行数据传输的输入输出流
- 通过RealInterceptorChain.proceed(…) 传递
while(true){
if (++followUpCount > MAX_FOLLOW_UPS) { // 20次
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
}
BridgeInterceptor
- 添加头部信息等操作,将request请求转化为能够进行网络访问的请求
- 将这个符合条件的Request进行网络请求
- 将网络请求返回的响应Response进行处理(压缩、解压等),转化为用户可用的Response
@Override public Response intercept(Chain chain) throws IOException {
...
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
// 保持长连接
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// 发起网络请求
Response networkResponse = chain.proceed(requestBuilder.build()); // 获取Response
// 得到最终的response
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
// 判断是否支持Gzip压缩
// 判断是否相应头是否支持Gzip压缩,(响应体内容是否经过了Gzip压缩)
// 判断Http头部是否有body体
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
// 将输入流转换成解压后的数据流
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
}
缓存策略
使用缓存
client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.readTimeout(READ_TIMEOUT_MILLIS,TimeUnit.MILLISECONDS)
.writeTimeout(WRITE_TIMEOUT_MILLIS,TimeUnit.MILLISECONDS)
.cache(new Cache(new File("cache"),24*1024*1024))
.build();
okhttp缓存核心:DiskLruCache
- okhttp维护了一个清理的线程池,由它进行着对缓存文件的清理和管理工作
Cache类
Cache.put方法
@Nullable CacheRequest put(Response response) {
String requestMethod = response.request().method();
...
// 只缓存GET方法
if (!requestMethod.equals("GET")) {
return null;
}
// Entry:需要缓存信息的包装类 包括url、请求头部、响应行、响应头部
Entry entry = new Entry(response);
DiskLruCache.Editor editor = null;
try {
// 创建editor,cache为DiskLruCache
// 传入url的MD5加密
editor = cache.edit(key(response.request().url()));
if (editor == null) {
return null;
}
// 开始缓存
entry.writeTo(editor);
// CacheRequestImpl:缓存响应主体
return new CacheRequestImpl(editor);
} catch (IOException e) {
abortQuietly(editor);
return null;
}
}
Cache.get方法
@Nullable Response get(Request request) {
// 解密得到key值
String key = key(request.url());
// 缓存“快照”
DiskLruCache.Snapshot snapshot;
Entry entry;
try {
// 如果没有这个“快照”就返回为空
snapshot = cache.get(key);
if (snapshot == null) {
return null;
}
} catch (IOException e) {
// Give up because the cache cannot be read.
return null;
}
try {
// 构建一个Entry
entry = new Entry(snapshot.getSource(ENTRY_METADATA));
} catch (IOException e) {
Util.closeQuietly(snapshot);
return null;
}
// 通过entry.response方法获取Response
Response response = entry.response(snapshot);
// 判断是否匹配
if (!entry.matches(request, response)) {
Util.closeQuietly(response.body());
return null;
}
return response;
}
// Entry.response 方法
public Response response(DiskLruCache.Snapshot snapshot) {
String contentType = responseHeaders.get("Content-Type");
String contentLength = responseHeaders.get("Content-Length");
// 恢复缓存的request
Request cacheRequest = new Request.Builder()
.url(url)
.method(requestMethod, null)
.headers(varyHeaders)
.build();
return new Response.Builder()
.request(cacheRequest)
.protocol(protocol)
.code(code)
.message(message)
.headers(responseHeaders)
// CacheResponseBody 用来对响应体的读取
.body(new CacheResponseBody(snapshot, contentType, contentLength))
.handshake(handshake)
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(receivedResponseMillis)
.build();
}