okhttp详解1

okhttp是square推出的网络开源库,支持http语义缓存,支持spdy和http2.0协议。看源码的目的主要是了解http协议实现,最重要是了解okhhtp的优劣,http2.0/spdy协议多路复用技术,多路复用技术与keep—alive的区别。

接下来首先分析主体流程,直接看代码 (此版本是okhttp2.6) , okhttp中包含okio库,okio是一个增强的io库, sink简单理解为OutputStream,source理解为InputStreame。

一、总体设计图

OkHttp

二、流程图

OkHttp

三、代码

1.Call.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.getDispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.getDispatcher().finished(this);
}
}

主要将请求存放到Dispatcher中,同步请求应该可以忽略这个部分。

2.ApplicationInterceptorChain.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override 
public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);

if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}

return interceptedResponse;
}

// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}

这里判断是否有拦截器,如果有拦截器则交给拦截器处理,拦截器的作用就是调试(Facebook stetho)。如果没有拦截器则直接处理。

3.Call.java

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
Response getResponse(Request request, boolean forWebSocket) throws IOException {
// Copy body metadata to the appropriate request headers.
RequestBody body = request.body();
if (body != null) {
Request.Builder requestBuilder = request.newBuilder();

MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}

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 = requestBuilder.build();
}

// Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null);

int followUpCount = 0;
while (true) {
if (canceled) {
engine.releaseConnection();
throw new IOException("Canceled");
}

try {
engine.sendRequest();
engine.readResponse();
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
HttpEngine retryEngine = engine.recover(e);
if (retryEngine != null) {
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e.getLastConnectException();
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
HttpEngine retryEngine = engine.recover(e, null);
if (retryEngine != null) {
engine = retryEngine;
continue;
}

// Give up; recovery is not possible.
throw e;
}

Response response = engine.getResponse();
Request followUp = engine.followUpRequest();

if (followUp == null) {
if (!forWebSocket) {
engine.releaseConnection();
}
return response;
}

if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}

if (!engine.sameConnection(followUp.httpUrl())) {
engine.releaseConnection();
}

Connection connection = engine.close();
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null,
response);
}
}

这里是数据请求的核心流程,包括发送请求、读取数据和异常重试(重试机制不做了解)。