OkHttp

使用

Get a URL

1
2
3
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute()

可以看到,完成一个Get请求需要OkHttpClient类型对象和Request类型对象。

建造者模式

简介

这种类型的设计模式属于创建型模式,它提供了一种创建对象的方式。

建造者模式关注该对象是如何一步一步创建而成的,对于用户而言,无须知道创建过程和内部组成细节。

OkHttpClient的创建

由构造器直接创建

1
public final OkHttpClient client = new OkHttpClient();

由Builder创建

1
2
3
4
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.build();

一步步来看这个过程,首先new 了一个OkHttpClient.Builder(),即一个Builder类型的对象,这个Builder是OkHttpClient的内部类。

Builder的构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}

其实就是对Builder中的参数进行初始化赋值操作。

Builder#addInterceptor()
1
2
3
4
5
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}

addInterceptor方法为Builder中的List interceptors类型的interceptors添加值,返回的是原先的Builder类型对象

Builder#cache()
1
2
3
4
5
public Builder cache(@Nullable Cache cache) {
this.cache = cache;
this.internalCache = null;
return this;
}

cache方法同理,对Builder中的cache参数赋值。返回的依旧是原先的Builder类型对象。

这些方法其实和常见的set方法很像,但最大的区别是,通常的set方法不返回任何值,仅仅可以一次调用。而Builder类中的这些方法依旧返回在原先基础上修改过的Builder对象,所以我们可以在这个Builder对象上继续修改,实现我们所见的连续调用操作。

但至此,我们只是构建好了Builder类型对象,这与OkHttpClient的创建有什么联系呢?

在用Builder创建时,除了调用上述的两个返回值类型为Builder的方法,还调用了build方法。

Builder#build()
1
2
3
public OkHttpClient build() {
return new OkHttpClient(this);
}

这个方法的返回值是OkHttpClient。它调用了OkHttpClient的构造方法,并将自身作为参数传入。

OkHttpClient(Builder builder)

用Builder的参数为OkHttpClient赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;

boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}

if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}

if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}

this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;

if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}

其实,OkHttpClient的无参构造方法也是调用了参数为Builder的构造方法,只是它传入的是默认创建的Builder

1
2
3
public OkHttpClient() {
this(new Builder());
}

由newBuilder创建

你可以使用newBuilder()来定制一个共享的OkHttpClient实例。这将构建共享相同连接池、线程池和配置的客户机。使用构建器方法为特定目的配置派生客户机。

1
2
OkHttpClient eagerClient = client.newBuilder().
readTimeout(500, TimeUnit.MILLISECONDS).build();

client是已有的OkHttpClient实例,newBuilder是OkHttpClient类中的方法

OkHttpClient#newBuilder()
1
2
3
public Builder newBuilder() {
return new Builder(this);
}

将自身作为参数传给Builder的构造方法

Builder(OkHttpClient okHttpClient)

用OkHttpClient对象的参数初始化Builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.callTimeout = okHttpClient.callTimeout;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}

建造者模式的优点

  • 用于创建对象Builder其实是可以共享的,我们可以用同一个Builder类型的对象创建出不同的实例。
  • 可以让用户不需要关心构造的具体细节,只需要指定想更改的参数。

OkHttp中的Request也用来建造者模式

责任链模式

简介

将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。

责任链模式有两种情况,一种是要求具体的处理对象只能选择承担责任或把责任继续传递,不能承担责任后又继续将责任往下传。另一种是,一个责任可以被不同的对象接收,即不论当前对象是否处理该责任,都可以终止传递或继续传递。

引言

使用OkHttp进行Get请求的代码如下

1
Response response = client.newCall(request).execute()

client.newCall(request)将会返回一个Call类型的实例,实际为RealCall类的对象。它的execute()方法如下

RealCall#execute()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}

正常情况下,它的返回值是 getResponseWithInterceptorChain()。这个方法就涉及到了责任链模式

RealCall#getResponseWithInterceptorChain()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
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));
//0表示index
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}

首先将所有拦截器放入一个list中,传给RealInterceptorChain构建对象。然后调用Response response = chain.proceed(originalRequest);即可得到response。

RealInterceptorChain#proceed()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}

// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}

if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}

return response;
}

这里的interceptors是构造RealInterceptorChain时传入的拦截器list

这个方法将会调用当前index的Interceptor实例,将

1
2
3
// Call the next interceptor in the chain.
RealInterceptorChain next = newRealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//这里的index+1了

作为参数传给Interceptor的intercept方法得到response。

接下来看一下不同的Interceptor的intercept方法

RetryAndFollowUpInterceptor#intercept()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//.....

Response response;
boolean success = false;
try {
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.

}
//.....
}

response = realChain.proceed(request, transmitter, null);

继续调用chain的proceed方法。也就是上面说的RealInterceptorChain的proceed方法。

BridgeInterceptor#intercept()

1
2
3
4
5
6
7
8
9
10
11
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();

//...

Response networkResponse = chain.proceed(requestBuilder.build());
//...

return responseBuilder.build();
}

和上面的Interceptor一样,继续调用chain的proceed()

小结

其他的Interceptor也同理。无论它们其中的逻辑多复杂,都会涉及到对chain的proceed()方法调用,这样,request就一层一层地传递下去了。

且每个Interceptor的response都与chain的proceed()的返回值有关。这个返回值其实是下一个拦截器的response,下一个的response同理。这样就可以通过连续调用,在最开始的chain中得到调用过所有拦截器后的response了。

注意我这里说的是当前Interceptor的response与它后面的Interceptor的response有关,并不是等于的关系。因为当前的Interceptor往往还会根据获得的response进行进一步的操作,才得到最终要返回的response。

所以一个Interceptor的操作分为三个步骤,准备,发送,解析结果。

拦截器的作用

  • addInterceptor(Interceptor),这是由开发者设置的,会按照开发者的要求,在所有的拦截器处理之前进行最早的拦截处理,比如一些公共参数,Header都可以在这里添加。

  • RetryAndFollowUpInterceptor——失败和重定向拦截器

  • BridgeInterceptor——封装request和response拦截器

  • CacheInterceptor——缓存相关的过滤器,负责读取缓存直接返回、更新缓存

  • ConnectInterceptor——连接服务,负责和服务器建立连接

  • CallServerInterceptor——执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据 进行http请求报文的封装与请求报文的解析f

详解各个拦截器

这里只关注拦截器的intercept方法

RetryAndFollowUpInterceptor

intercept
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Override public Response intercept(Chain chain) throws IOException {
//chain.request()的request是构建chain时传入的
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();

int followUpCount = 0;
Response priorResponse = null;
while (true) {
transmitter.prepareToConnect(request);

if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}

Response response;
boolean success = false;
try {
// 继续调用后面的拦截器
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// 试图通过路由连接失败。请求还没有发送
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// 试图与服务器通信失败。请求可能已经发送。
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// 网络调用抛出一个异常。释放任何资源。
if (!success) {
transmitter.exchangeDoneDueToException();
}
}

//如果存在之前的response,请用之前的response构建它。这样的response没有body
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}

Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;

//根据response及其状态码判断是否进行重试或重定向。如果不需要,则返回null。否则找出响应接收userResponse的HTTP请求。然后添加身份验证头、遵循重定向或处理客户端请求超时。
Request followUp = followUpRequest(response, route);
// 如果followUp为空,表示不需要执行重试或者重定向,直接返回数据
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}


RequestBody followUpBody = followUp.body();
// 如果followUp为null,followUpBody不为空,并且只需要请求一次时,那么就返回response;
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}

closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}

// 判断重试或者重定向的次数是否超过最大的次数,是的话则抛出异常;
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}

// 将需要重试或者重定向的请求赋值给新的请求;
request = followUp;
priorResponse = response;
}
}

BridgeInterceptor

intercept
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  @Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();

RequestBody body = userRequest.body();
if (body != null) {
//封装request的Content-Type
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
//封装request的Content-Length
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
//封装request的Host
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
//封装request的Connection
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}

//如果我们添加了一个“Accept-Encoding: gzip”报头字段,我们也要负责解压传输流。
boolean transparentGzip = false;
//如果Accept-Encoding为null且Range为null,为Accept-Encoding添加gzip字段
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//封装request的Cookie
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
//封装request的User-Agent
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}

Response networkResponse = chain.proceed(requestBuilder.build());

HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//transparentGzip是之前设置的标志位
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
//删除一些headers
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)));
}

return responseBuilder.build();
}

CacheInterceptor

intercept
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
@Override public Response intercept(Chain chain) throws IOException {
//这里的cache是client.internalCache(),是构造CacheInterceptor的参数
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;//1

long now = System.currentTimeMillis();

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();//2
//传向network的request, 如果为 null 表示不使用网络
Request networkRequest = strategy.networkRequest;

//用于返回或验证的cached response;如果为null表示不使用缓存
Response cacheResponse = strategy.cacheResponse;

//跟踪满足缓存策略的HTTP响应。
if (cache != null) {
cache.trackResponse(strategy);
}

if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body());//因为cacheResponse == null,表示不使用缓存。 所以关闭cacheCandidate
}

// 如果不允许使用网络,并且不使用缓存,返回504状态,body为空
//Unsatisfiable Request (only-if-cached)
if (networkRequest == null && cacheResponse == null) {
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();
}

// 如果不需要使用网络数据或者禁止使用网络,那么就直接返回缓存的数据;
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.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}

// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
// 如果缓存数据不为空并且进行的请求code为304,表示数据没有变化,继续使用缓存数据;
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
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();

//在合并headers之后,在stripping Content-Encoding报头之前更新缓存
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}

Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();

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;
}

这个intercept方法涉及很多知识点,我们逐步分析了解

强制缓存和协商缓存

强制缓存就是服务器会告诉客户端该怎么缓存,例如cache-Control 字段,随便举几个例子:

  • private:所有内容只有客户端可以缓存,Cache-Control的默认取值
  • max-age=xxx:表示缓存内容将在xxx秒后失效
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存

协商缓存就是需要客户端和服务器进行协商后再决定是否使用缓存,比如强制缓存过期失效了,就要再次请求服务器,并带上缓存标志,例如Etag。
客户端再次进行请求的时候,请求头带上If-None-Match,也就是之前服务器返回的Etag值。

Etag值就是文件的唯一标示,服务器通过某个算法对资源进行计算,取得一串值(类似于文件的md5值),之后将该值通过etag返回给客户端

然后服务器就会将Etag值和服务器本身文件的Etag值进行比较,如果一样则数据没改变,就返回304,代表你要请求的数据没改变,你直接用就行啦。
如果不一致,就返回新的数据,这时候的响应码就是正常的200。

注释1
1
Response cacheCandidate = cache != null? cache.get(chain.request()) : null;

这里的cache来自client.internalCache(),是构造CacheInterceptor的参数。

注释2
1
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();

new CacheStrategy.Factory(now, chain.request(), cacheCandidate)生成了CacheStrategy.Factory对象。

CacheStrategy.Factory()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;

if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
Headers headers = cacheResponse.headers();
//解析headers构建Factory
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}

CacheStrategy.Factory#get()

1
2
3
4
5
6
7
8
9
10
public CacheStrategy get() {
CacheStrategy candidate = getCandidate();
//onlyIfCached表示不要使用网络
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
// We're forbidden from using the network and the cache is insufficient.
return new CacheStrategy(null, null);
}

return candidate;
}

CacheStrategy.Factory#getCandidate()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//返回要使用的策略(假设request可以使用网络)。
private CacheStrategy getCandidate() {
// No cached response.
if (cacheResponse == null) {
return new CacheStrategy(request, null);
}

// Drop the cached response if it's missing a required handshake.
if (request.isHttps() && cacheResponse.handshake() == null) {
return new CacheStrategy(request, null);
}

//如果这个response不应该被存储,那么它就不应该被用作response源。只要持久性存储良好且规则不变,此检查就应该是多余的。
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}

CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}

CacheControl responseCaching = cacheResponse.cacheControl();

//强制缓存的检查
long ageMillis = cacheResponseAge();
long freshMillis = computeFreshnessLifetime();

if (requestCaching.maxAgeSeconds() != -1) {
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}

long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) {
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}

long maxStaleMillis = 0;
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
}
// 判断强制缓存是否有效,是的话就返回缓存数据;
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}

//找到一个要添加到请求中的条件。
//如果存在协商缓存,这些缓存是从cache中读出来的,具体在Factory里
// 协商缓存的处理
String conditionName;
String conditionValue;
// etag协商缓存
if (etag != null) {
conditionName = "If-None-Match";
conditionValue = etag;
}
// Last-Modified协商缓存
else if (lastModified != null) {
conditionName = "If-Modified-Since";
// 最后修改时间
conditionValue = lastModifiedString;
}
// Last-Modified协商缓存
else if (servedDate != null) {
conditionName = "If-Modified-Since";
// 服务器最后修改时间
conditionValue = servedDateString;
}
// 没有协商缓存,返回一个空的Response的CacheStrategy;
else {
return new CacheStrategy(request, null); // 没有满足协商缓存的条件,返回常规的请求。
}

Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
return new CacheStrategy(conditionalRequest, cacheResponse);
}

ConnectInterceptor

封装了socket连接和TLS握手等逻辑

intercept()
1
2
3
4
5
6
7
8
9
10
11
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();

// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);

return realChain.proceed(request, transmitter, exchange);
}

连接的具体实现在transmitter.newExchange(chain, doExtensiveHealthChecks);方法中。

CallServerInterceptor

在上一个拦截器中建立完连接后,就需要在这个拦截器中进行发数据与读数据的工作了。

intercept()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();

long sentRequestMillis = System.currentTimeMillis();

exchange.writeRequestHeaders(request);

boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
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.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
responseBuilder = exchange.readResponseHeaders(true);
}

if (responseBuilder == null) {
if (request.body().isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
exchange.noRequestBody();
if (!exchange.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.
exchange.noNewExchangesOnConnection();
}
}
} else {
exchange.noRequestBody();
}

if (request.body() == null || !request.body().isDuplex()) {
exchange.finishRequest();
}

if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}

if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}

Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

code = response.code();
}

exchange.responseHeadersEnd(response);

if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}

if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}

if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}

return response;
}

具体进行向服务器发送header和body以及接收服务器返回的数据的操作。

核心工作都由 HttpCodec 对象完成,而 HttpCodec 实际上利用的是 Okio,而 Okio 实际上还是用的 Socket