OkHttp网络框架4:拦截器 解析
OkHttp 拦截器。 责任链模式。
拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。
拦截器不区分同步、 异步。
1.OkHttp core 系统内部提供的拦截器。
RetryAndFollowUpInterceptor 重试失败重定向拦截器。
↓ ↑
BridgeInterceptor 桥接适配拦截器
↓ ↑
CacheInterceptor 缓存拦截器
↓ ↑
ConnectInterceptor 连接拦截器,负责建立可用连接。
↓ ↑
CallServerInterceptor. http请求写入网络IO流,读取返回的数据。
2.拦截器的作用。
2.1 同步请求。
// RealCall.java
client.dispatcher().executed(this); //加入同步请求队列中
Response result = getResponseWithInterceptorChain();
//执行完毕
client.dispatcher().finished(this); //
private Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>(); //创建一个拦截器列表
interceptors.addAll(client.interceptors()); //优先处理自定义拦截器
interceptors.add(retryAndFollowUpInterceptor); //失败重连拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar())); //接口桥接拦截器(同时处理cookie逻辑)
interceptors.add(new CacheInterceptor(client.internalCache())); //缓存拦截器
interceptors.add(new ConnectInterceptor(client)); //分配连接拦截器
if (!retryAndFollowUpInterceptor.isForWebSocket()) { //web的socket连接的网络配置拦截器
interceptors.addAll(client.networkInterceptors());
}
//最后是连接服务器发起真正的网络请求的拦截器
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//流式执行并返回response
return chain.proceed(originalRequest);
}
//RealInterceptorChain.java
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed() {
//核心代码,拦截器的链
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index+1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptor.get(index);
Response response = interceptor.intercept(next);
}
//RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) { //【重点方法】
Request request = chain.request();
//...
response = realChain.proceed(request, streamAllocation, null,null);
return response;
}
拦截器总结1:
1.创建一系列拦截器,并将其放入一个拦截器list中;
2.创建一个拦截器链 RealInterceptorChain, 并执行拦截器的 proceed方法。
拦截器总结2:
- 在发起请求前对request 进行处理;
- 调用下一个拦截器,获取response;
- 对response进行处理,返回给上一个拦截器。
3.每个拦截器的作用。
3.1 retryAndFollowUpInterceptor 失败重连拦截器
public Response intercept(Chain chain) { //【重点方法】
Request request = chain.request();
//获取连接服务端的connection,用于与服务端用于数据传输的输入输出流。最终传给ConnectInterceptor
streamAllocation = new StreamAllocation(client.connectkionPool(), createAddress(request.url()),
call, eventListener, callStackTrace );
//...
while(true){
response = realChain.proceed(request, streamAllocation, null,null);
//...
if(++followUpCount > MAX_FOLLOW_UPS) {//L168,不能无限制的重试网络。MAX_FOLLOW_UPS=20
streamAllocation.release();
}
}
return response;
}
总结:
- 创建 streamAllocation 对象
- 调用 RealInterceptorChain.proceed() 进行网络请求
- 根据异常结果或 响应结果判断是否要进行重新请求。
- 调用下一个拦截器,对response进行处理,返回给上一个拦截器。
3.2 BridgeInterceptor
设置内容长度,编码方式,压缩解压缩, 添加头部信息等。
public Response intercept(Chain chain) { //【重点方法】
Request userRequest = chain.request();
Response networkResponse = chain.proceed(requestBuilder.build());
//相当于获取解压后的数据
GzipSource responseBody = new GzipSource();
return responseBuilder.build();
}
总结:
- 负责将用户构建的一个Request 请求转换为能够进行网络访问的请求。
- 将这个符合网络请求的Request 进行网络请求;
- 将网络请求回来的Response 转化为用户可用的Response。
3.3 CacheInterceptor 缓存拦截器
要使用这个功能要给OkHttpClient 设置cache。
new OkHttpClient.Builder()
.cache(new Cache(new File("cache"), 1010241024))
.build();
// Cache.java
CacheRequest put(Response response){
String requestMethod = response.request().method();
if(!requestMtthod.equals("GET")) return null; // 不是Get请求 返回null。
Entry entry = new Entry(response); //Entry用于包装缓存的信息
DiskLruCache.Editor editor = cache.edit(key(response.request().url()));
entry.writeTo(editor); //缓存写到磁盘上
return new CacheRequestImpl(editor); //CacheRequestImpl的body是响应主体。数据缓存。
}
Response get(Request request) { //从缓存中读取response。
String key = key(request.url()); //key用于解密
DiskLruCache.Snapshot snapshot = cache.get(key); //获得缓存的值
Entry entry = new Entry(snapshot.getSource(ENTRY_METADATA));
Response response = entry.response(snapshot);
//是否匹配,不匹配返回null
if(!entry.matches(request, response)) {
Util.closeQuitely(response.body());
return null;
}
return response(response)
}
// 缓存拦截器 CacheInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidator = cache!=null ? cache.get(chain.request()) :null;
CacheStrategy strategy = new CacheStrategy.Factory(now,chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
// 没网络,且没缓存,构建一个504错误。
// 没网络,直接返回网络缓存结果。
Response networkResponse = chiain.proceed(networkRequest);
// 304从缓存中读取数据。
if(HttpHeaders.hadBody(response) && CacheStrategy.isCacheable(...)) {
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if(HttpMethod.invalidatesCache(networkRequest.method())) {
cache.remove(networkRequest);
}
}
//CacheStrategy.java
get()--> getCandidate()-->
Response.Builder build = cacheResponse.newBuilder();
return new CacheStrategy(null, builder.build());
3.4 连接池 ,拦截器 ConnectInterceptor
打开与 服务器之间的连接,正式开启网络请求。
ConnectInterceptor(client)
@Override public Response intercept(Chain chain) throws IOException {
RealChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();//获取前面的变量,来自重试重定向拦截器
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//HttpCodec 处理请求和返回数据。
HttpCodec httpCodec = streamAllocation.netStream(client,chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();//【关键对象】做实际的网络传输
return realChain.proceed(request,streamAllocation, httpCodec, connection);
}
总结:
- ConnectInterceptor获取Interceptor传过来的 StreamAllocation,StreamAllocation.newStream() 得到HttpCodec。
- 将刚才创建的用于网络IO的RealConnection对象, 以及对于与服务器交互最为关键的 HttpCodec等对象传递给后面的拦截器.
//newStream()方法。
public HttpCodec new Stream() {
RealConnection resultConnection = findHeallthyConnection();// findConnection()
HttpCodec resultCodec = resultConnection.newCodec(client, chain,this);
return resultCodec;
}
findConnection()作用。
1. 尝试获取connection,如果可以复用就复用;不能复用就从连接池获取新的RealConnection连接。
2. 将新的连接,放到连接池中。
okhttp中连接方式分类:Tunnel 隧道连接、Socket连接。
3.4.1 连接池的操作。ConnectionPool 维护网络连接,管理连接的复用。
在一定时间内,复用连接。能有效的清理回收
//ConnectionPool.java
RealConnection get(Address address, StreamAllocation streamAllocation) {
for(RealConnectioin connection: connections) {
if(connection.isEligible(address, route)) { //判断连接是否可用
streamAllocation.acquire(connection, true);//获取连接
return connection;
}
}
return null;
}
public void acquire(RealConnection connection, boolean reportAcquired) {
this.connection =connection;
this.reportAcquired =reportAcquired;
connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
//用于判断集合的大小,根据集合大小,来判断每个连接是否超过了最大连接数。
}
// put方法。
void put(RealConnection connection) {
if(!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunning);//回收connection时使用。
}
connections.add(connection); // 加入队列中
}
总结:
- 每次请求,都会在重试重定向拦截器,产生一个StreamAllocation对象;
- StreamAllocation对象 的弱引用,添加到RealConnection 对象的 allocations 集合中。
- 从连接池中获取,来复用。
3.4.1 自动回收 connection
Gc的回收算法,统计StreamAllocation的数量。
有个独立的线程 cleanUpRunnable 线程。
// ConnectionPool.java
private final Runnable cleanUpRunnable = new Runnable(0 {
@Override public void run(0 {
while(true) {
long waitNanos = cleanup(System.nanoTime());//GC回收算法,标记清除算法
if(waitNanos == -1) return;
if(waitNanos > 0) {
long waitMillis = waitNanos/1000000L;
waitNanos -= (waitMillis *1000000L);
synchronized (ConnectionPool.java) {
ConnectionPool.this.wait(waitMillis, (int)waitNanos);
}
}
}
}
}
// 后续方法:
pruneAndGetAllocationCount();
总结:
- okHttp使用了GC回收算法;
- StreamAllocation 的数量会渐渐变成0;
- 被线程池检测到并回收,这样就可以保持多个健康的keep-alive 连接。
3.5 CallServerInterceptor
1.真正服务器发起请求。2.接收到服务器给我们的响应。再返回。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain =(RealInterceptorChain)chain;//链工具
HttpCodec httpCodec =realChain.httpStream();//编码Request,解码response输出
StreamAllocation streamAllocation = realChain.streamAllocation();//分配stream
RealConnection connection =(RealConnection)realChain.connection();//抽象链接的具体实现
Request request = realChain.request();//网络请求
httpCodec.writeRequestHeaders(request);//1.写入请求头部header信息
request.body().writeTo(bufferedRequestBody); //2.写入请求的body信息。
httpCodec.finishRequest(); //3.完成了网络请求的写入工作。
//读取工作。
responseBuilder = httpCodec.readResponseHeaders(false);//4.读取网络响应的头部信息
response = response.newBuilder()
.body(httpCodec.openResponseBody(response)).build();//5.读取网络响应的body信息
//禁止新的流创建,StreamAllocation.noNewsStreams();
return response;//完成response的获取工作。
}
okhttp一次网络请求的大致过程
- Call对象对请求的封装。
- Dispatcher对请求的分发
- getResponseWithInterceptors方法,这里面是拦截器链,里面有几个拦截器。
- RetryAndFollowUpInterceptor 负责重试和重定向请求;
- CacheInterceptor 处理缓存的拦截器;
- BridgeInterceptor 负责okHttp 请求和响应对象 与实际http协议当中的 请求和响应对象的转换。
- ConnectionInterceptor 负责建立连接,和流对象的。
- CallServerInterceptor 负责完成最终的网络请求。发送请求+读取响应。
----------End------------------------