1.OkHttp源码
1.1.RealCall.getResponseWithInterceptorChain方法解析
首先看一个典型的同步请求过程
public String get(String url) throws IOException {
//新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
//新建一个Request对象
Request request = new Request.Builder().url(url).build();
//Response为OKHttp中的响应
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
}else{
throw new IOException("Unexpected code " + response);
}
}
/**
* Prepares the {@code request} to be executed at some point in the futur e.
* 准备将要被执行的request
*/
@Override
public Call newCall(Request request) {
return new RealCall(this, request);
}
上面的client.newCall(request)返回的是RealCall,RealCall才是真正的请求执行者,来看 下RealCall的构造方法和execute方法。
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(clien t);
}
@Override
public Response execute() throws IOException {
synchronized (this) {
//检查这个 call是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一 样的 call,可以利用 all#clone 方法进行克隆
if (executed)
throw new IllegalStateException("Already Executed");
executed = true;
}
try {
//不过度关注
client.dispatcher.executed(this);
// 真正发出网络请求,解析返回结果的地方‐‐需要关注的地方
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
//不过度关注,返回执行状态
client.dispatcher.finished(this);
}
}
//拦截器的责任链。
private Response getResponseWithInterceptorChain() throws
IOException {
// Build a full stack of interceptors.
List<Interceptor>
interceptors = new ArrayList<>();
//在配置 OkHttpClient 时设置的 interceptors;
interceptors.addAll(client.interceptors());
//负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
interceptors.add(retryAndFollowUpInterceptor);
//负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友 好的响应的 BridgeInterceptor
//其实也就是给request和response添加header
interceptors.add(new
BridgeInterceptor(client.cookieJar()));
//负责读取缓存直接返回、更新或者写入缓存的CacheInterceptor
interceptors.add(new CacheInterceptor(client.internalCache()));
//负责和服务器建立连接的ConnectInterceptor,比较复杂
interceptors.add(new ConnectInterceptor(client));
if
(!retryAndFollowUpInterceptor.isForWebSocket()) {
//配置 OkHttpClient 时设置的
networkInterceptors interceptors.addAll(client.networkInterceptors());
}
//负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor,也 不深入研究和okio相关
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new
RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
//开始链式调用
return chain.proceed(originalRequest);
}
public Response proceed(Request request, StreamAllocation streamAllocatio n, HttpCodec httpCodec, 2Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
//***************略***************
// Call the next interceptor in the chain.、
//这里是责任链模式调用
//实例化下一个拦截器对应的RealIterceptorChain对象 RealInterceptorChain
next = new RealInterceptorChain(10interceptors, streamAllocation, httpCodec,
connection, index + 1, reque st);
//得到当前的拦截器
Interceptor interceptor =
interceptors.get(index);
//调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对 象传递下去 Response
response = interceptor.intercept(next);
//***************略***************
return response;
}
// 看看interceptor.intercept(next)干了啥,又调用了chain.proceed地柜迭代啊
//核心代码,调用下一个拦截器
response =((RealInterceptorChain)chain).proceed(request, streamAllocat ion,
null,null);
1.2自定义一个仿okhttp的不纯的责任链demo 上面拦截器责任链模式我们可以自己写个demo加深理解,就以我们平常的请假申请流程为 例,员工提交申请(request)--主管审批--经理审批--总监审批到此流程完成给你 resposne。
public interface ModelOkHttpInterceptor {
//处理实际的任务
public void intercept(Chain chain);
interface Chain {
//构建责任链
void proceed(String user);
}
}
public class RealChain implements ModelOkHttpInterceptor.Chain {
private List<ModelOkHttpInterceptor> mInterceptors;
private int mIndex;
private String mUser;
public RealChain(List<ModelOkHttpInterceptor> interceptors, int index, String user) {
mInterceptors = interceptors;
mIndex = index;
mUser = user;
}
@Override
public void proceed(String user) {
mUser = user;
if (mIndex > mInterceptors.size()) {
System.out.print("错误,没有人能够审批了");
}
RealChain chain = new RealChain(mInterceptors, mIndex + 1, mUser);
ModelOkHttpInterceptor interceptor = mInterceptors.get(mIndex);
interceptor.intercept(chain);
}
public String getUser() {
return mUser;
}
}
public class StaffInterceptor implements ModelOkHttpInterceptor {
@Override
public void intercept(Chain chain) {
String name = ((RealChain) chain).getUser();
System.out.println(name + "提交加班申请");
//员工提交申请,电子流责任链转到主管那里
chain.proceed("主管");
}
}
public class SupervisorInterceptor implements ModelOkHttpInterceptor {
@Override
public void intercept(Chain chain) {
String name = ((RealChain) chain).getUser();
System.out.println(name + "审批通过");
//主管神品通过申请,电子流责任链转到部门经理那里
chain.proceed("经理");
}
}
public class ManagerInterceptor implements ModelOkHttpInterceptor {
@Override
public void intercept(Chain chain) {
String name = ((RealChain) chain).getUser();
System.out.println(name + "审批通过");
//部门经理提交申请,电子流责任链转到总监那里
chain.proceed("总监");
}
}
public class ChiefInterceptor implements ModelOkHttpInterceptor {
@Override
public void intercept(Chain chain) {
String name = ((RealChain) chain).getUser();
//一般总监审批通过流程就结束了,这里就相当于request结束有response返回
System.out.println(name + "审批通过");
}
}
public class MainTest {
public static void main(String[] args) {
List<ModelOkHttpInterceptor> interceptorList = new ArrayList<>();
interceptorList.add(new StaffInterceptor());
interceptorList.add(new SupervisorInterceptor());
interceptorList.add(new ManagerInterceptor());
interceptorList.add(new ChiefInterceptor());
//构建加班申请责任链
ModelOkHttpInterceptor.Chain realChain = new RealChain(interceptorList, 0, "老三");
//员工开始提交申请
realChain.proceed("老三");
}
}
2.几个主要的拦截器解析
2.1RetryAndFollowUpInterceptor:负责失败重试以及重定向拦截器 这个拦截器负责:当一个请求由于某种原因获取响应数据失败了,就回去尝试重连恢复,并 且重新封装request(followUpRequest)最大重试次数20次。
/**
* This interceptor recovers from failures and follows redirects as neces sary.
* 负责失败重试以及重定向的
* It may throw an {@link IOException} if the call was canceled.
*/
public final class RetryAndFollowUpInterceptor implements Interceptor {
/**
* How many redirects and auth challenges should we attempt? Chrome follo ws 21 redirects; Firefox,
* curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.10 * 重试的最大尝试次数,超过将抛出异常
*/
private static final int MAX_FOLLOW_UPS = 20;
//OkHttpClient成员
private final OkHttpClient client;
//这个类暂时先放过
private StreamAllocation streamAllocation;
//这个也先放过
private boolean forWebSocket;
//请求被取消
private volatile boolean canceled;
public RetryAndFollowUpInterceptor(OkHttpClient client) {
this.client = client;
}
/**
* 同步的立即取消socket连接请求
*/
public void cancel() {
canceled = true;
StreamAllocation streamAllocation = this.streamAllocation;
if (streamAllocation != null) streamAllocation.cancel();
}
//*******************省略了一些set方法**********************
/**
* 主要爱分析这个方法
*
* @param chain
* @return
* @throws IOException
*/
@Override
public Response intercept(Chain chain) throws IOException {
//拿到拦截链中的请求
Request request = chain.request();
//连接池,获取一个正确的连接
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
//重定向次数初始化为0
int followUpCount = 0;
Response priorResponse = null;
//开始循环
while (true) {
//请求被取消直接抛异常
if (canceled) {
//连接被释放
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
//核心代码,调用下一个拦截器
response = ((RealInterceptorChain) chain).proceed(request, streamAlloca tion, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
// 连接路由尝试失败,请求将不会被发送
if (!recover(e.getLastConnectException(), true, request)) throw e.getLa
stConnectException();
releaseConnection = false;
//重试啊
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
//和服务器交互失败,请求也不会被发送
if (!recover(e, false, request)) throw e;
releaseConnection = false;
//重试啊
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
// 抛出未知异常,释放资源
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
//如果上一次重试获取的response存在,将其boby置为空(没看明白干嘛)
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//新的重试request封装:添加请求头,添加连接超时
Request followUp = followUpRequest(response);
//followUp为空(看下面followUpRequest这个方法的注释如果follow‐up不必要或者 不可用就返回空)
//重试流程结束,在这里跳出。
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
//超过最大重试次数,释放连接,抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow‐up requests: " + followUpC ount);
}
//异常处理
if (followUp.body() instanceof UnrepeatableRequestBody) {
throw new HttpRetryException("Cannot retry streamed HTTP body", respons e.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()));
} 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;
}
}
//*******************省略一些方法**********************
/**
* Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
* either add authentication headers, follow redirects or handle a clien t request timeout. If a
* follow‐up is either unnecessary or not applicable, this returns null.
* 对重试请求做了下封装:添加header,连接timeout
*/
private Request followUpRequest(Response userResponse) throws IOExcepti on {
if (userResponse == null) throw new IllegalStateException();
Connection connection = streamAllocation.connection();
Route route = connection != null
? connection.route()
: null;
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
return client.proxyAuthenticator().authenticate(route, userResponse);
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// "If the 307 or 308 status code is received in response to a request other than GET
// or HEAD, the user agent MUST NOT automatically redirect the request"
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall‐through
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// Does the client allow redirects?
if (!client.followRedirects()) return null;
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
// Don't follow redirects to unsupported protocols.
if (url == null) return null;
// If configured, don't follow redirects between SSL and non‐SSL.
boolean sameScheme = url.scheme().equals(userResponse.request().url().s cheme());
if (!sameScheme && !client.followSslRedirects()) return null;
// Redirects don't include a request body.
Request.Builder requestBuilder = userResponse.request().newBuilder();
if (HttpMethod.permitsRequestBody(method)) {
193 if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
requestBuilder.method(method, null);
}
198 requestBuilder.removeHeader("Transfer‐Encoding");
requestBuilder.removeHeader("Content‐Length");
requestBuilder.removeHeader("Content‐Type");
}
// When redirecting across hosts, drop all authentication headers. This 204 // is potentially annoying to the application layer since they have no
// way to retain them.
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}
return requestBuilder.url(url).build();
case HTTP_CLIENT_TIMEOUT:
// 408's are rare in practice, but some servers like HAProxy use this r esponse code. The
// spec says that we may repeat the request without modifications. Mode rn browsers also
// repeat the request (even non‐idempotent ones.) 216 if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
return userResponse.request();
default:
return null;
}
}
/**
* Returns true if an HTTP request for {@code followUp} can reuse the co nnection used by this
* engine.
* 判断request请求是否可以复用
*/
private boolean sameConnection(Response response, HttpUrl followUp) {
HttpUrl url = response.request().url();
return url.host().equals(followUp.host())
&& url.port() == followUp.port()
&& url.scheme().equals(followUp.scheme());
}
/**
* Bridges from application code to network code. First it builds a netwo rk request from a user * request. Then it proceeds to call the network. Finally it builds a use r response from the network * response.
* 上面意思是根据用户请求构建网络请求,使得它继续去访问网络,最后根据网络响应构 建用户响应。 * 其实就是卫request添加请求头,为response添加响应头。
*/
public final class BridgeInterceptor implements Interceptor {
private final CookieJar cookieJar;
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
0 if (body != null) {
MediaType contentType = body.contentType(); //添加contenttype
if (contentType != null) {
requestBuilder.header("Content‐Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != ‐){
//如果body有值,请求头设置body的内容长度,并且移除传输编码
requestBuilder.header("Content‐Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer‐Encoding");
} else{
// 否则,设置传输编码(这里是分块传输编码),移除内容长度值
requestBuilder.header("Transfer‐Encoding", "chunked");
requestBuilder.removeHeader("Content‐Length");
}
} //添加Host
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
} //添加Connection
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep‐Alive");
} // If we add an "Accept‐Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept‐Encoding") == null) {
transparentGzip = true;
requestBuilder.header("Accept‐Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User‐Agent") == null) {
requestBuilder.header("User‐Agent", Version.userAgent());
} //开始给resposne添加header
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkRespons e.headers());
Response.Builder responseBuilder = networkResponse.newBuilder().request(userRequest);
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);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
/**
* Returns a 'Cookie' HTTP request header with all cookies, like {@cod e a=b; c=d}.
*/
private String cookieHeader(List<Cookie> cookies) {
StringBuilder cookieHeader = new StringBuilder();
for (int i = 0, size = cookies.size(); i < size; i++) {
if (i > 0) {
cookieHeader.append("; ");
}
Cookie cookie = cookies.get(i);
cookieHeader.append(cookie.name()).append('=').append(cookie.value());
}
return cookieHeader.toString();
}
}
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@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, doExtensiveHea lthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connecti on);
}
}
//主要看看findHealthyConnection()的实现
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealth Checks) {
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try { //findHealthyConnection这个方法是重点
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec;
if (resultConnection.httpConnection != null) {
resultCodec = new HttpCodec(client, this, resultConnection.httpConnec tion);
} else {
resultConnection.socket().setSoTimeout(readTimeout);
resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
resultCodec = new HttpCodec(client, this, resultConnection.source, resultConnection.sink);
} synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
private RealConnection findHealthyConnection(int connectTimeout, int read Timeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, w riteTimeout, pingIntervalMillis, connectionRetryEnabled); // If this is a brand new connection, we can skip the extensive health c hecks. synchronized (connectionPool) { if (candidate.successCount == ) { return candidate; } } // Do a (potentially slow) check to confirm that the pooled connection is still good. If it // isn't, take it out of the pool and start again. if (!candidate.isHealthy(doExtensiveHealthChecks)) {//判断连接是否可用 noNewStreams();//连接不可用,移除 continue;//不可用,就一直持续 } return candidate; } } private RealConnection findConnection(int connectTimeout, int readTimeou t, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOExcept ion { boolean foundPooledConnection = false; RealConnection result = null; Route selectedRoute = null;
Connection releasedConnection;
Socket toClose;
//异常情况,直接抛出
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled)
throw new IOException("Canceled");
// Attempt to use an already‐allocated connection. We need to be carefu l here because our
// already‐allocated connection may have been restricted from creating new streams.
releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) { //
// 经过releaseIfNoNewStreams,connection不为null,则连接是可用的
// We had an already‐allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as re leased!
releasedConnection = null;
}
//无可用连接,去连接池connectionPool中获取
if (result == null) {
// Attempt to get a connection from the pool.
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);
}
if (result != null) {
//上面通过去连接池中找,如果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 operatio n.
// 如果在连接池中也没找到可用连接, 就需要一个路由信息,这是一个阻塞操作
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 gett ing a connection from
// the pool. This could match due to connection coalescing.
// 提供address,再次从连接池中获取连接
List<Route> routes = routeSelection.getAll();
for (int i = ,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;
}
}
}
}
//提供路路由信息,然后进行查找可用链接,还是没有找到可用链接,就需要生成一个 新的连接
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
} // Create a connection and assign it to this allocation immediately. Th is makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount =; result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
} // If we found a pooled connection on the nd time around, we're done.
// 如果连接是从连接池中找到的,直接拿出来使用
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
} // Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalM illis, connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
// 将新生成的连接放入连接池中
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created co ncurrently, then
// release this connection and acquire that one.
//如果是一个http连接,http连接应具有多路复用特性,
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
2.4CacheInterceptor
public final class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException { //获取到缓存‐‐‐‐第一步
Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
long now = System.currentTimeMillis();
//CacheStrategy缓存策略类,管理request和response的缓存‐‐‐‐第二步
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 appl icable. Close it.
}
// If we're forbidden from using the network and the cache is insuffici ent, fail.
// 如果禁止访问网络,并且无缓存,返回‐‐‐‐第三步
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder().request(chain.request()).protocol(Protocol.HTTP__).code().message("Unsatisfiable Request (only‐if‐cached)").body(EMPTY_BODY).sentRequestAtMillis(‐L).receivedResponseAtMillis(System.currentTimeMillis()).build();
}
// If we don't need the network, we're done.
// 没有网络请求,直接返回缓存‐‐还是第三步
if (networkRequest == null) {
return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();
}
Response networkResponse = null;
try {
//网络请求拦截器‐‐‐‐第四步
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
// 如果因为io或者其他原因崩溃,不泄露缓存并关闭
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
// 如果已经存在缓存response,并且正在做条件get请求
if (cacheResponse != null) {
if (validate(cacheResponse, networkResponse)) {
Response response = cacheResponse.newBuilder().headers(combine(cacheResponse.headers(), networkResponse.headers())).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();
if (HttpHeaders.hasBody(response)) {
CacheRequest cacheRequest = maybeCache(response, networkResponse.reques t(), cache);
//写入缓存‐‐‐‐第六步
response = cacheWritingResponse(cacheRequest, response);
} return response;
}
}
2.5CallServerInterceptor:发送和接收数据
public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;
public CallServerInterceptor(boolean forWebSocket) {
this.forWebSocket = forWebSocket;
}
@Override
public Response intercept(Chain chain) throws IOException {
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).stre amAllocation();
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 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();
} //异常情况处理
int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() >) {
throw new ProtocolException("HTTP " + code + " had non‐zero Content‐Length: " + response.body().con tentLength());
} return response;
}
}
3.请求过程
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = ,size = responseHeaders.size();
i<size ;
i++){
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.val ue(i));
} System.out.println(response.body().string());
}
});
}
和同步不同的地方主要是enqueue,看这个方法:
//异步任务使用
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
} //调用了上面我们没有详细说的Dispatcher类中的enqueue(Call )方法.接着继续看‐‐分 析
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正 在准备中的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//运行中的异步请求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
//同 步请求 /** **分析 **/
synchronized void enqueue(AsyncCall call) {
//如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到 runningAsyncCalls中执行,同时利用线程池
// 执行call;否者将call加入到readyAsyncCa lls中
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
//call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类‐‐ 分析
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
} //异步请求 /** **分析 **/
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override
protected void execute() {
boolean signalledCallback = false;
try {
//破案了,异步请求也是调用getResponseWithInterceptorChain处理来获得respons e,
// 异步任务同样也是会经过一系列的拦截器
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
4.自定义一个拦截器(LogInterceptor) 就以最常见的LoggingInterceptor为例子
class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOEx ception {
Request request = chain.request();
//.请求前‐‐打印请求信息
long t = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
//.网络请求
Response response = chain.proceed(request);
//.网络响应后‐‐打印响应信息
long t = System.nanoTime();
logger.info(String.format("Received response for %s in %.fms%n%s", response.request().url(), (t ‐ t)
/ ed, response.headers()))
;
return response;
}
}
4.1添加 Application Interceptor,request是最初的request,response 就是最终的响 应,得到日志相对较少
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User‐Agent", "OkHttp Example").build();
Response response = client.newCall(request).execute(); response.body().
close();
响应数据:
INFO:
Sending request
http://www.publicobject.com/helloworld.txt on null
User‐Agent: OkHttp Example
INFO: Received response for https://publicobject.com/helloworld.txt in .ms
Server: nginx/.. (Ubuntu)
Content‐Type: text/plain
Content‐Length: Connection: keep‐alive
4.2添加 NetworkInterceptor,可以得到的是更多的信息,重定向的信息也会得到。
OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new LoggingInterceptor()).build();
Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User‐Agent", "OkHttp Example").build();
Response response = client.newCall(request).execute();
response.body().
close();
INFO:
Sending request
http://www.publicobject.com/helloworld.txt on Conn ection{www.publicobject.com:, proxy=DIRECT hostAddress=... ciph erSuite=none protocol=http/.}
User‐Agent:
OkHttp Example
Host:
www.publicobject.com Connection:Keep‐
Alive Accept‐Encoding:gzip
INFO:
Received response for http://www.publicobject.com/helloworld.txt i n .ms Server: nginx/.. (Ubuntu) Content‐Type: text/html
Content‐Length:Connection:keep‐
alive Location:https://publicobject.com/helloworld.txt
INFO:
Sending request
https://publicobject.com/helloworld.txt on Connect ion{publicobject.com:, proxy=DIRECT hostAddress=... cipherSuit e=TLS_ECDHE_RSA_WITH_AES__CBC_SHA protocol=http/.}
User‐Agent:
OkHttp Example
Host:
publicobject.com Connection:Keep‐
Alive Accept‐Encoding:gzip
INFO: Received response for https://publicobject.com/helloworld.txt in .ms Server: nginx/.. (Ubuntu) Content‐Type: text/plain Content‐Length: Connection: keep‐alive