写在前面
OkHttp版本3.11.0
前置知识
责任链模式
简单使用
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Test1Interceptor())
.addNetworkInterceptor(new Test2Interceptor())
.build();
Request request = new Request.Builder()
.url("https://github.com")
.build();
//execute, 同步执行
Response response = client.newCall(request).execute();
//enqueue 异步执行
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
public class Test1Interceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//此处可以对request(请求信息)添加自己的逻辑
//比如添加header
//修改host url
Response response = chain.proceed(request);
//此处可以对response(返回结果)添加自己的逻辑
//模拟接口返回结果
return response;
}
}
public class Test2Interceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//此处可以对request(请求信息)添加自己的逻辑
//比如添加header
//修改host url
Response response = chain.proceed(request);
//此处可以对response(返回结果)添加自己的逻辑
//模拟接口返回结果
return response;
}
}
从后往前看, 最后一个方法是okhttp3.Call#enqueue, 接口里的方法, 所以, 我们看okhttp3.OkHttpClient#newCall
OkHttpClient#newCall
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
okhttp3.RealCall#newRealCall
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
看到这里, 我们发现, 这里实际上就是创建了RealCall, 它是Call的实现类, 最后执行它的okhttp3.RealCall#enqueue方法, 实现异步接口调用. 所以我们看下okhttp3.RealCall#enqueue
okhttp3.RealCall#enqueue
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
//如果已经执行过, 就抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//关键代码
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
首先, okhttp3.OkHttpClient#dispatcher方法返回的是okhttp3.Dispatcher, 然后执行okhttp3.Dispatcher#enqueue
所以, 我们分析的重点, 变成了okhttp3.Dispatcher#enqueue
okhttp3.Dispatcher#enqueue
synchronized void enqueue(AsyncCall call) {
//立即执行的条件
//1. 正在执行请求的数量小于maxRequests(默认值64)
//2.正在执行的请求中, 跟本次请求call的host url一样 的call的数量小于maxRequestsPerHost(默认值5)
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//加到正在执行的队列中(第1个条件要用到runningAsyncCalls)
runningAsyncCalls.add(call);
//交给线程池执行call
executorService().execute(call);
} else {
//加入到等待队列中等待执行
readyAsyncCalls.add(call);
}
}
到这里我们知道了, enqueue方法, 最后实际上是把AsyncCall 交给线程池执行. 所以, 我们接下来看AsyncCall
AsyncCall是NamedRunnable的子类, 而NamedRunnable实现了Runnable, 并把Runnable#run中执行了抽象方法NamedRunnable#execute. 实际的执行就是okhttp3.RealCall.AsyncCall#execute
okhttp3.RealCall.AsyncCall#execute
@Override protected void execute() {
boolean signalledCallback = false;
try {
//!!! 关键方法
//获取请求结果
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//通知dispatcher, call已经执行完
//dispatcher把call从正在执行的请求队列中移除call
client.dispatcher().finished(this);
}
}
这里的关键就是Response response = getResponseWithInterceptorChain();
getResponseWithInterceptorChain方法直接就返回了结果response. 所以, 真正的请求应该发生在getResponseWithInterceptorChain方法中.
RealCall#getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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));
//注意第5个参数index传了0
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这个方法做的就是构建Interceptor.Chain对象, 并执行chain.proceed方法.
这个方法也是我们理解OkHttp的拦截器Interceptor机制的第一个关键点
需要注意的是, 列表interceptors中我们依次传入了我们设置的 interceptors/retryAndFollowUpInterceptor/BridgeInterceptor/CacheInterceptor/ConnectInterceptor/networkInterceptors/CallServerInterceptor.这里他们具体做了什么, 我们先放一边, 继续看chain.proceed. 这里的chain实际上是RealInterceptorChain.
RealInterceptorChain#proceed
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
//实际执行了这个方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//...检查参数
//构建RealInterceptorChain
//注意第5个参数, 这里我们的index是0(getResponseWithInterceptorChain方法传入了0),
//所以, 新构建的RealInterceptorChain 中的index变成了1
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//取出第0个interceptor,
//这个时候我们回头重新看下RealCall#getResponseWithInterceptorChain
//如果我们设置了interceptor , 那就会是我们通过OkHttpClient.Builder#addInterceptor设置的interceptor,
//如果我们没设置interceptor, 此处就是retryAndFollowUpInterceptor
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//...结果检查
return response;
}
像示例中, 我们通过OkHttpClient.Builder#addInterceptor设置的Test1Interceptor, 此时, 就会执行我们创建的Test1Interceptor#intercept方法.在该方法中, 我们执行chain.proceed来获取结果, 也即是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);
//取出第1个interceptor, 也就是retryAndFollowUpInterceptor
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//...结果检查
return response;
}
RetryAndFollowUpInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
//这里才真正创建了StreamAllocation, 前面都是null
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
//执行Chain#proceed
//第一次执行这里, 让Chain继续往下执行
response = realChain.proceed(request, streamAllocation, null, null);
//拿到response 后, 做其他逻辑
releaseConnection = false;
//...异常处理
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
//followUpRequest方法, 对各个请求响应码code做处理
//需要重试的返回新的request
//不需要重试的返回null
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
//如果不需要重试, 就返回结果
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
//开始重试处理(请求失败重新请求)的逻辑
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
//如果重试了20次, 结束重试
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
//如果不是相同的链接, 比如发生了请求重定向?
//重新构建StreamAllocation
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
//赋值新的request
request = followUp;
priorResponse = response;
//继续while循环
//因为有新的request和streamAllocation
//重新执行while, 走response = realChain.proceed(request, streamAllocation, null, null)等逻辑
//实现重新请求
}
}
简述逻辑如下:
1.执行realChain.proceed, 从chain拿到请求结果response
2.对response 响应码做判断, 如果不需要重试(比如请求成功), 就返回response
3.如果需要重定向, 就构建新的request和streamAllocation, 然后重新执行步骤1, 直到重试了20次或者抛出了异常
暂时, 我们先不考虑失败的情况. 这里我们继续看RealInterceptorChain#proceed
此时index已经变成了2(因为构建RealInterceptorChain的时候, 其参数index是上一个RealInterceptorChain的index+1)
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//此时index已经变成了2
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//取出第2个interceptor, 也就是BridgeInterceptor
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//...结果检查
return response;
}
BridgeInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
//...添加request的请求头
//比如使用gzip/Content-Type/Connection等等
Response networkResponse = chain.proceed(requestBuilder.build());
//对结果添加额外处理
//...比如使用GZIP对数据进行解压(如果请求头设置了gzip的话)
return responseBuilder.build();
}
简述逻辑如下:
1.对request添加请求头信息, 比如gzip/Cookie/Host/Content-Length
2.执行chain.proceed, 从chain获取结果response
3.处理response(比如使用GZIP解压缩)生成新的response并返回
这里chain.proceed也是执行的RealInterceptorChain#proceed, 逻辑和之前的类似, 也是获取interceptor, 执行interceptor#intercept. 这次interceptor是CacheInterceptor
我们可以小结一下RealInterceptorChain和Interceptor
1.RealCall#getResponseWithInterceptorChain中我们构建了interceptors列表
2.index从0开始, 每次index+1, 创建RealInterceptorChain, 并从interceptors取出index对应位置的interceptor, 执行interceptor#intercept
3.在Interceptor#intercept中执行RealInterceptorChain#proceed, 创建index+1的RealInterceptorChain. 在RealInterceptorChain#proceed前, 我们可以对request添加额外的处理, 在RealInterceptorChain#proceed后, 我们可以对response添加额外的处理.
4.关键是index, 这样依次实现了interceptors的遍历
看起来很麻烦, 其实就是遍历interceptors
下面我们继续看CacheInterceptor
CacheInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
//1.cache没命中, 但又设置了只使用缓存
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
//只有一种情况会出现networkRequest 和cacheResponse 都为空的情况
//请求头设置了only-if-cached, 表示只使用cache缓存, 但是cache并没有命中
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
//2. cache命中, cache数据有效
// If we don't need the network, we're done.
if (networkRequest == null) {
//表示cache命中, 直接使用cache, 不再使用网络请求
//比如响应头中有immutable, 表示数据不会改变
//又比如, 当前时间并没超过响应头里的有效时间
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//3. 继续执行网络请求
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
//4.如果响应码是304, 表示数据没变, 可以使用上次缓存的数据
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//拿到缓存的数据构建response 并返回
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//5.如果自定义了InternalCache或者Cache, 就就交给InternalCache或者Cache做缓存处理
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
对于cache的各个判断在CacheStrategy.Factory#getCandidate中, 详细逻辑可以看这个方法.
对于CacheInterceptor#intercept, 简述如下:
1.如果cache没命中, 并且只能使用缓存的情况, 设置响应码为504, 构建Response返回
2.如果cache有效命中, 用cache构建Response返回
3.使用chain.proceed取数据
4.如果响应码是304, 表示数据没变cache可用, 使用cache构建新的Response返回
5.如果设置了自定义cache, 则交给自定义cacha完成缓存
下一个执行的Interceptor是ConnectInterceptor. 我们继续看ConnectInterceptor#intercept
ConnectInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//streamAllocation 在RetryAndFollowUpInterceptor#intercept中创建
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 , 也获取了connection
//这样, 下一步streamAllocation.connection()才会返回正确的RealConnection
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
如果不深究connection 以及connection pool. 这里就只是构建httpCodec 和connection .
connection 会从连接池(connection pool)获取, 如果连接池没有, 就新建RealConnection, 并把新建的RealConnection 放进连接池.
这里我们简单看下如何获取connection.
StreamAllocation#newStream -> StreamAllocation#findHealthyConnection -> StreamAllocation#findConnection
我们重点看下StreamAllocation#findConnection方法
StreamAllocation#findConnection
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
//...
synchronized (connectionPool) {
//...异常处理
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new streams.
releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
// We had an already-allocated connection and it's good.
//如果StreamAllocation已经有了connection(比如失败重试的时候)
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
//如果StreamAllocation中connection还没初始化(还是空)
if (result == null) {
// Attempt to get a connection from the pool.
//从connectionPool中获取connection
//注意, Internal.instance的实例是在OkHttpClient的static代码块中完成创建的
//其get方法是pool.get(address, streamAllocation, route), 也就是从connectionPool获取connection
//这里是用address去连接池中匹配
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
result = connection;
} else {
selectedRoute = route;
}
}
}
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
//如果已经获取到connection 就返回
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
//前面用host url没匹配到
//这里我们用ip地址去连接池匹配(一个host可以对应多个ip)
List routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
//如果用ip还是匹配不到, 就直接创建RealConnection
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
//直接创建RealConnection
result = new RealConnection(connectionPool, selectedRoute);
//给StreamAllocation#connection赋值
acquire(result, false);
}
}
//如果是从连接池中获取到的connection 就返回
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
//如果是新建的, 就执行RealConnection#connect, 初始化连接 最终会执行socket.connect
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
//把完成初始化的connection 放进连接池
// Pool the connection.
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
简述如下:
1.使用address去连接池中查找获取connection, 如果找到就返回
2.使用address被DNS解析到的ip去连接池中查找, 如果找到就返回
3.如果还没找到就创建
4.对新建的connection初始化(执行RealConnection#connect完成初始化), Socket也是在此时创建的
5.把完成初始化的connection放进连接池
在继续往下看之前, 我们有必要再看一看RealConnection#connect -> RealConnection#connectSocket
RealConnection#connectSocket
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
//新建socket对象
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
//socket连接
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
// The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
// More details:
// https://github.com/square/okhttp/issues/3245
// https://android-review.googlesource.com/#/c/271775/
try {
//通过Okio获取socket的输入流
source = Okio.buffer(Okio.source(rawSocket));
//通过Okio获取socket的输出流
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
}
需要注意的是, 通过Okio, 我们获取到了socket的输入流source和输出流sink
下面, 我们看下一个Interceptor.
这里执行到了通过addNetworkInterceptor添加的Interceptor, demo里是Test2Interceptor.
到这里我们也可以回答一个问题: addInterceptor和addNetworkInterceptor有什么不同?
不同之处就是, 位置不通, addInterceptor方法添加的Interceptor的Interceptor#intercept方法先执行, addNetworkInterceptor后执行
demo中, Test2Interceptor只是简单实现. 没有别的操作, 所以我们继续往下看
下一个Interceptor也是最后一个, 是CallServerInterceptor
CallServerInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
//这里的httpCodec实际上是Http1Codec
//Http1Codec#writeRequestHeaders只干了一件事情, 就是通过sink写入数据, 但此时socket并没有发送数据, 因为没用sink.flush
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
//如果请求类型不是GET和HEAD, 并且body不为空(比如上传文件)
//针对需要上传body的情况
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
//针对100-continue的特殊处理
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
//如果请求头里Expect被设置成了100-continue
//那就直接请求(不带body)
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
//类似写入, 这里就是通过source读出socket返回的数据
//读出返回的数据
//如果返回100, responseBuilder 就是空
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
//这里有两种情况
//①使用了100-continue, 并且服务器响应了100
//②没使用100-continue
//这里做的就是把body(比如文件)写进sink
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//写进sink
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
//socket发送数据
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//从source读数据
responseBuilder = httpCodec.readResponseHeaders(false);
}
//构建Response
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
//对response的特殊code(100/101/204/205)做额外的处理
//...
return response;
}
简述流程如下:
1.buffer(sink)里写入请求信息
2.如果不是GET和HEAD并且有body(比如上传文件)
① Expect设置了100-continue, 直接发送请求, 如果服务器返回100(表示服务器可以接收请求体)
②没设置100-continue
就在buffer(sink)里写入body数据
3.socket发送buffer(sink)里的数据
4.读取socket返回的数据(source), 构建成response
这里需要注意的是, CallServerInterceptor#intercept没有像其它Interceptor执行Chain.proceed, 也即是Chain到CallServerInterceptor就走到了最后, CallServerInterceptor通过socket执行了真正的网络请求, 并把结果封装成了response返回.
总结
1. 拦截器原理: 责任链模式
自定义拦截器+OkHttp默认实现的RetryAndFollowUpInterceptor/BridgeInterceptor/CacheInterceptor/ConnectInterceptor/CallServerInterceptor共同构成链.
2. 各个拦截器作用:
RetryAndFollowUpInterceptor:失败重试
BridgeInterceptor:①添加公共请求头信息②使用GZIP解压数据(如果使用了GZIP的话)
CacheInterceptor:①如果缓存有效, 直接返回缓存数据, 不再请求 ②缓存返回的结果
ConnectInterceptor:①从连接池获取RealConnection②如果连接池里没获取到, 就创建新的RealConnection, 并且初始化socket
CallServerInterceptor:①通过socket发送请求数据②读取返回数据封装成response