研究了一下HttpURLConnection的源码:
在使用的时候都是通过URL.openConnection()来获取HttpURLConnection对象,然后调用其connect方法进行链接,所以先从URL.penConnection()入手:
/** * Returns a new connection to the resource referred to by this URL. * * @throws IOException if an error occurs while opening the connection. */public URLConnection openConnection() throws IOException { return streamHandler.openConnection(this);}
接下来就要看一下streamHandler究竟是何方神圣?我们搜一下他的赋值,实在setupStreamHandler方法中进行的:
/** * Sets the receiver's stream handler to one which is appropriate for its * protocol. * * Note that this will overwrite any existing stream handler with the new * one. Senders must check if the streamHandler is null before calling the * method if they do not want this behavior (a speed optimization). * * @throws MalformedURLException if no reasonable handler is available. */
void setupStreamHandler() { streamHandler = streamHandlers.get(protocol); if (streamHandler != null) { return; } if (streamHandlerFactory != null) { streamHandler = streamHandlerFactory.createURLStreamHandler(protocol); if (streamHandler != null) { streamHandlers.put(protocol, streamHandler); return; } } String packageList = System.getProperty("java.protocol.handler.pkgs"); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (packageList != null && contextClassLoader != null) { for (String packageName : packageList.split("\\|")) { String className = packageName + "." + protocol + ".Handler"; try { Class<?> c = contextClassLoader.loadClass(className); streamHandler = (URLStreamHandler) c.newInstance(); if (streamHandler != null) { streamHandlers.put(protocol, streamHandler); } return; } catch (IllegalAccessException ignored) { } catch (InstantiationException ignored) { } catch (ClassNotFoundException ignored) { } } } if (protocol.equals("file")) { streamHandler = new FileHandler(); } else if (protocol.equals("ftp")) { streamHandler = new FtpHandler(); } else if (protocol.equals("http")) { try { String name = "com.android.okhttp.HttpHandler"; streamHandler = (URLStreamHandler) Class.forName(name).newInstance(); } catch (Exception e) { throw new AssertionError(e); } } else if (protocol.equals("https")) { try { String name = "com.android.okhttp.HttpsHandler"; streamHandler = (URLStreamHandler) Class.forName(name).newInstance(); } catch (Exception e) { throw new AssertionError(e); } } else if (protocol.equals("jar")) { streamHandler = new JarHandler(); } if (streamHandler != null) { streamHandlers.put(protocol, streamHandler); }}
- 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
这里我们就以HTTP协议为例说一下所以找到okhttp HttpHandler.openConnection()方法:
package com.squareup.okhttp;import java.io.IOException;import java.net.Proxy;import java.net.URL;import java.net.URLConnection;import java.net.URLStreamHandler;public final class HttpHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { return new OkHttpClient().open(url); } @Override protected URLConnection openConnection(URL url, Proxy proxy) throws IOException { if (url == null || proxy == null) { throw new IllegalArgumentException("url == null || proxy == null"); } return new OkHttpClient().setProxy(proxy).open(url); } @Override protected int getDefaultPort() { return 80; }}
- 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
接下来就悲剧了,因为我找不到OkHttpClient()类中有open方法。
仔细查看了文档后发现在OKHttp1.6.0的时候该方法就已经已经过时了。
@Deprecatedpublic HttpURLConnection open(URL url)Deprecated. moved to OkUrlFactory.open.
那我们怎么往下分析呢?很显然Android sdk中使用的OkHttp不是最新版。所以我们可以使用1.5.0版本的OKHttp接着分析。
在项目build.gradle中进行配置 compile ‘com.squareup.okhttp:okhttp:1.5.0’ 然后开始愉快的查看源码。
package com.squareup.okhttp;import com.squareup.okhttp.internal.Util;import com.squareup.okhttp.internal.http.HttpAuthenticator;import com.squareup.okhttp.internal.http.HttpURLConnectionImpl;import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;import com.squareup.okhttp.internal.http.ResponseCacheAdapter;import com.squareup.okhttp.internal.okio.ByteString;import com.squareup.okhttp.internal.tls.OkHostnameVerifier;import java.io.IOException;import java.net.CookieHandler;import java.net.HttpURLConnection;import java.net.Proxy;import java.net.ProxySelector;import java.net.ResponseCache;import java.net.URL;import java.net.URLConnection;import java.net.URLStreamHandler;import java.net.URLStreamHandlerFactory;import java.security.GeneralSecurityException;import java.util.ArrayList;import java.util.List;import java.util.concurrent.TimeUnit;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;/** Configures and creates HTTP connections. */public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable { private final RouteDatabase routeDatabase; private Proxy proxy; private List protocols; private ProxySelector proxySelector; private CookieHandler cookieHandler; private OkResponseCache responseCache; private SSLSocketFactory sslSocketFactory; private HostnameVerifier hostnameVerifier; private OkAuthenticator authenticator; private ConnectionPool connectionPool; private boolean followProtocolRedirects = true; private int connectTimeout; private int readTimeout; public OkHttpClient() { routeDatabase = new RouteDatabase(); } /** * Sets the default connect timeout for new connections. A value of 0 means no timeout. * * @see URLConnection#setConnectTimeout(int) */ public void setConnectTimeout(long timeout, TimeUnit unit) { if (timeout < 0) { throw new IllegalArgumentException("timeout < 0"); } if (unit == null) { throw new IllegalArgumentException("unit == null"); } long millis = unit.toMillis(timeout); if (millis > Integer.MAX_VALUE) { throw new IllegalArgumentException("Timeout too large."); } connectTimeout = (int) millis; } /** Default connect timeout (in milliseconds). */ public int getConnectTimeout() { return connectTimeout; } /** * Sets the default read timeout for new connections. A value of 0 means no timeout. * * @see URLConnection#setReadTimeout(int) */ public void setReadTimeout(long timeout, TimeUnit unit) { if (timeout < 0) { throw new IllegalArgumentException("timeout < 0"); } if (unit == null) { throw new IllegalArgumentException("unit == null"); } long millis = unit.toMillis(timeout); if (millis > Integer.MAX_VALUE) { throw new IllegalArgumentException("Timeout too large."); } readTimeout = (int) millis; } /** Default read timeout (in milliseconds). */ public int getReadTimeout() { return readTimeout; } /** * Sets the HTTP proxy that will be used by connections created by this * client. This takes precedence over {@link #setProxySelector}, which is * only honored when this proxy is null (which it is by default). To disable * proxy use completely, call {@code setProxy(Proxy.NO_PROXY)}. */ public OkHttpClient setProxy(Proxy proxy) { this.proxy = proxy; return this; } public Proxy getProxy() { return proxy; } /** * Sets the proxy selection policy to be used if no {@link #setProxy proxy} * is specified explicitly. The proxy selector may return multiple proxies; * in that case they will be tried in sequence until a successful connection * is established. * * If unset, the {@link ProxySelector#getDefault() system-wide default} * proxy selector will be used. */
public OkHttpClient setProxySelector(ProxySelector proxySelector) { this.proxySelector = proxySelector; return this; } public ProxySelector getProxySelector() { return proxySelector; } /** * Sets the cookie handler to be used to read outgoing cookies and write * incoming cookies. * * If unset, the {@link CookieHandler#getDefault() system-wide default} * cookie handler will be used. */
public OkHttpClient setCookieHandler(CookieHandler cookieHandler) { this.cookieHandler = cookieHandler; return this; } public CookieHandler getCookieHandler() { return cookieHandler; } /** * Sets the response cache to be used to read and write cached responses. */ public OkHttpClient setResponseCache(ResponseCache responseCache) { return setOkResponseCache(toOkResponseCache(responseCache)); } public ResponseCache getResponseCache() { return responseCache instanceof ResponseCacheAdapter ? ((ResponseCacheAdapter) responseCache).getDelegate() : null; } public OkHttpClient setOkResponseCache(OkResponseCache responseCache) { this.responseCache = responseCache; return this; } public OkResponseCache getOkResponseCache() { return responseCache; } /** * Sets the socket factory used to secure HTTPS connections. * * If unset, a lazily created SSL socket factory will be used. */
public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } public SSLSocketFactory getSslSocketFactory() { return sslSocketFactory; } /** * Sets the verifier used to confirm that response certificates apply to * requested hostnames for HTTPS connections. * * If unset, the * {@link javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier() * system-wide default} hostname verifier will be used. */
public OkHttpClient setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Sets the authenticator used to respond to challenges from the remote web * server or proxy server. * * If unset, the {@link java.net.Authenticator#setDefault system-wide default} * authenticator will be used. */
public OkHttpClient setAuthenticator(OkAuthenticator authenticator) { this.authenticator = authenticator; return this; } public OkAuthenticator getAuthenticator() { return authenticator; } /** * Sets the connection pool used to recycle HTTP and HTTPS connections. * * If unset, the {@link ConnectionPool#getDefault() system-wide * default} connection pool will be used. */
public OkHttpClient setConnectionPool(ConnectionPool connectionPool) { this.connectionPool = connectionPool; return this; } public ConnectionPool getConnectionPool() { return connectionPool; } /** * Configure this client to follow redirects from HTTPS to HTTP and from HTTP * to HTTPS. * * If unset, protocol redirects will be followed. This is different than * the built-in {@code HttpURLConnection}'s default. */
public OkHttpClient setFollowProtocolRedirects(boolean followProtocolRedirects) { this.followProtocolRedirects = followProtocolRedirects; return this; } public boolean getFollowProtocolRedirects() { return followProtocolRedirects; } public RouteDatabase getRoutesDatabase() { return routeDatabase; } /** * @deprecated OkHttp 1.5 enforces an enumeration of {@link Protocol protocols} * that can be selected. Please switch to {@link #setProtocols(java.util.List)}. */ @Deprecated public OkHttpClient setTransports(List transports) { List protocols = new ArrayList(transports.size()); for (int i = 0, size = transports.size(); i < size; i++) { try { Protocol protocol = Util.getProtocol(ByteString.encodeUtf8(transports.get(i))); protocols.add(protocol); } catch (IOException e) { throw new IllegalArgumentException(e); } } return setProtocols(protocols); } /** * Configure the protocols used by this client to communicate with remote * servers. By default this client will prefer the most efficient transport * available, falling back to more ubiquitous protocols. Applications should * only call this method to avoid specific compatibility problems, such as web * servers that behave incorrectly when SPDY is enabled. * * The following protocols are currently supported: *
* - http/1.1 *
- spdy/3.1 *
- HTTP-draft-09/2.0 *
* * This is an evolving set. Future releases may drop * support for transitional protocols (like spdy/3.1), in favor of their * successors (spdy/4 or http/2.0). The http/1.1 transport will never be * dropped. * *
If multiple protocols are specified, NPN will * be used to negotiate a transport. Future releases may use another mechanism * (such as ALPN) * to negotiate a transport. * * @param protocols the protocols to use, in order of preference. The list * must contain "http/1.1". It must not contain null. */
public OkHttpClient setProtocols(List protocols) { protocols = Util.immutableList(protocols); if (!protocols.contains(Protocol.HTTP_11)) { throw new IllegalArgumentException("protocols doesn't contain http/1.1: " + protocols); } if (protocols.contains(null)) { throw new IllegalArgumentException("protocols must not contain null"); } this.protocols = Util.immutableList(protocols); return this; } /** * @deprecated OkHttp 1.5 enforces an enumeration of {@link Protocol * protocols} that can be selected. Please switch to {@link * #getProtocols()}. */ @Deprecated public List getTransports() { List transports = new ArrayList(protocols.size()); for (int i = 0, size = protocols.size(); i < size; i++) { transports.add(protocols.get(i).name.utf8()); } return transports; } public List getProtocols() { return protocols; } public HttpURLConnection open(URL url) { return open(url, proxy); } HttpURLConnection open(URL url, Proxy proxy) { String protocol = url.getProtocol(); OkHttpClient copy = copyWithDefaults(); copy.proxy = proxy; if (protocol.equals("http")) return new HttpURLConnectionImpl(url, copy); if (protocol.equals("https")) return new HttpsURLConnectionImpl(url, copy); throw new IllegalArgumentException("Unexpected protocol: " + protocol); } /** * Returns a shallow copy of this OkHttpClient that uses the system-wide * default for each field that hasn't been explicitly configured. */ OkHttpClient copyWithDefaults() { OkHttpClient result = clone(); if (result.proxySelector == null) { result.proxySelector = ProxySelector.getDefault(); } if (result.cookieHandler == null) { result.cookieHandler = CookieHandler.getDefault(); } if (result.responseCache == null) { result.responseCache = toOkResponseCache(ResponseCache.getDefault()); } if (result.sslSocketFactory == null) { result.sslSocketFactory = getDefaultSSLSocketFactory(); } if (result.hostnameVerifier == null) { result.hostnameVerifier = OkHostnameVerifier.INSTANCE; } if (result.authenticator == null) { result.authenticator = HttpAuthenticator.SYSTEM_DEFAULT; } if (result.connectionPool == null) { result.connectionPool = ConnectionPool.getDefault(); } if (result.protocols == null) { result.protocols = Util.HTTP2_SPDY3_AND_HTTP; } return result; } /** * Java and Android programs default to using a single global SSL context, * accessible to HTTP clients as {@link SSLSocketFactory#getDefault()}. If we * used the shared SSL context, when OkHttp enables NPN for its SPDY-related * stuff, it would also enable NPN for other usages, which might crash them * because NPN is enabled when it isn't expected to be. * * This code avoids that by defaulting to an OkHttp created SSL context. The * significant drawback of this approach is that apps that customize the * global SSL context will lose these customizations. */
private synchronized SSLSocketFactory getDefaultSSLSocketFactory() { if (sslSocketFactory == null) { try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, null, null); sslSocketFactory = sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { throw new AssertionError(); } } return sslSocketFactory; } /** Returns a shallow copy of this OkHttpClient. */ @Override public OkHttpClient clone() { try { return (OkHttpClient) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } } private OkResponseCache toOkResponseCache(ResponseCache responseCache) { return responseCache == null || responseCache instanceof OkResponseCache ? (OkResponseCache) responseCache : new ResponseCacheAdapter(responseCache); } /** * Creates a URLStreamHandler as a {@link URL#setURLStreamHandlerFactory}. * * This code configures OkHttp to handle all HTTP and HTTPS connections * created with {@link URL#openConnection()}:
{@code * * OkHttpClient okHttpClient = new OkHttpClient(); * URL.setURLStreamHandlerFactory(okHttpClient); * }
*/ public URLStreamHandler createURLStreamHandler(final String protocol) { if (!protocol.equals("http") && !protocol.equals("https")) return null; return new URLStreamHandler() { @Override protected URLConnection openConnection(URL url) { return open(url); } @Override protected URLConnection openConnection(URL url, Proxy proxy) { return open(url, proxy); } @Override protected int getDefaultPort() { if (protocol.equals("http")) return 80; if (protocol.equals("https")) return 443; throw new AssertionError(); } }; }}
- 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
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
接着看一下HttpURLConnectionImpl类,它是HttpURLConnection的子类:
public class HttpURLConnectionImpl extends HttpURLConnection { .....}
到这里new URL(url).openConnection()方法已经分析完了,其实就是返回了一个HtppURLConnectionImpl对象。
我们在使用HttpURLConnection都是这样使用:
String url = "http://www.baidu.com"URL url = new URL(url);HttpURLConnection connection = (HttpURLConnection)url.openConnection();...connection.connect();...connection.getResponseCode();connection.getOuoputStream();connection.getInputStream();....
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
上面分析了new URL().openConnection()那我们这里就接着分析第二步了,就是调用connect()方法的处理:
这里看一下HttpURLConnectionImpl.connect()方法:
@Override public final void connect() throws IOException {initHttpEngine();boolean success;do { success = execute(false);} while (!success);}
接着看一下initHttpEngine()方法的实现:
private void initHttpEngine() throws IOException { if (httpEngineFailure != null) { throw httpEngineFailure; } else if (httpEngine != null) { return; } connected = true; try { if (doOutput) { if (method.equals("GET")) { method = "POST"; } else if (!HttpMethod.hasRequestBody(method)) { throw new ProtocolException(method + " does not support writing"); } } httpEngine = newHttpEngine(method, null, null); } catch (IOException e) { httpEngineFailure = e; throw e; } } private HttpEngine newHttpEngine(String method, Connection connection, RetryableSink requestBody) { Request.Builder builder = new Request.Builder() .url(getURL()) .method(method, null ); Headers headers = requestHeaders.build(); for (int i = 0; i < headers.size(); i++) { builder.addHeader(headers.name(i), headers.value(i)); } boolean bufferRequestBody; if (fixedContentLength != -1) { bufferRequestBody = false; builder.header("Content-Length", Long.toString(fixedContentLength)); } else if (chunkLength > 0) { bufferRequestBody = false; builder.header("Transfer-Encoding", "chunked"); } else { bufferRequestBody = true; } Request request = builder.build(); OkHttpClient engineClient = client; if (engineClient.getOkResponseCache() != null && !getUseCaches()) { engineClient = client.clone().setOkResponseCache(null); } return new HttpEngine(engineClient, request, bufferRequestBody, connection, null, requestBody); }
- 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
到这里我们知道他会创建一个HttpEngine类,我们先不管它,接着看一下execute()方法的内部实现:
/*** Sends a request and optionally reads a response. Returns true if the* request was successfully executed, and false if the request can be* retried. Throws an exception if the request failed permanently.*/private boolean execute(boolean readResponse) throws IOException {try { httpEngine.sendRequest(); route = httpEngine.getRoute(); handshake = httpEngine.getConnection() != null ? httpEngine.getConnection().getHandshake() : null; if (readResponse) { httpEngine.readResponse(); } return true;} catch (IOException e) { HttpEngine retryEngine = httpEngine.recover(e); if (retryEngine != null) { httpEngine = retryEngine; return false; } httpEngineFailure = e; throw e;}}
- 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
到这里,可以大胆的猜测一下了HttpEngine
应该就是实际在Socket
链接上进行数据收发的类。 当然这只是猜测,接着看一下它的实现:
/** * Handles a single HTTP request/response pair. Each HTTP engine follows this * lifecycle: * * - It is created. *
- The HTTP request message is sent with sendRequest(). Once the request * is sent it is an error to modify the request headers. After * sendRequest() has been called the request body can be written to if * it exists. *
- The HTTP response message is read with readResponse(). After the * response has been read the response headers and body can be read. * All responses have a response body input stream, though in some * instances this stream is empty. *
* * The request and response may be served by the HTTP response cache, by the * network, or by both in the event of a conditional GET. */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
文档验证了我们的想法。因为它内部实现代码比较多,所以就不全部贴了,按照重要的一步步分析,既然上面调用了sendRequest()
方法,这里就从他入手:
/*** Figures out what the response source will be, and opens a socket to that* source if necessary. Prepares the request headers and gets ready to start* writing the request body if it exists.*/public final void sendRequest() throws IOException { if (responseSource != null) return; if (transport != null) throw new IllegalStateException(); prepareRawRequestHeaders(); OkResponseCache responseCache = client.getOkResponseCache(); Response cacheResponse = responseCache != null ? responseCache.get(request) : null; long now = System.currentTimeMillis(); CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheResponse).get(); responseSource = cacheStrategy.source; request = cacheStrategy.request; if (responseCache != null) { responseCache.trackResponse(responseSource); } if (responseSource != ResponseSource.NETWORK) { validatingResponse = cacheStrategy.response; } if (cacheResponse != null && !responseSource.usesCache()) { closeQuietly(cacheResponse.body()); } if (responseSource.requiresConnection()) { if (connection == null) { connect(); } transport = (Transport) connection.newTransport(this); if (hasRequestBody() && requestBodyOut == null) { requestBodyOut = transport.createRequestBody(request); } } else { if (connection != null) { client.getConnectionPool().recycle(connection); connection = null; } this.response = validatingResponse; if (validatingResponse.body() != null) { initContentStream(validatingResponse.body().source()); } }}
- 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
接着看一下connect()
方法:
/** Connect to the origin server either directly or via a proxy. */private void connect() throws IOException { if (connection != null) throw new IllegalStateException(); if (routeSelector == null) { String uriHost = request.url().getHost(); if (uriHost == null || uriHost.length() == 0) { throw new UnknownHostException(request.url().toString()); } SSLSocketFactory sslSocketFactory = null; HostnameVerifier hostnameVerifier = null; if (request.isHttps()) { sslSocketFactory = client.getSslSocketFactory(); hostnameVerifier = client.getHostnameVerifier(); } Address address = new Address(uriHost, getEffectivePort(request.url()), sslSocketFactory, hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getProtocols()); routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(), client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase()); } connection = routeSelector.next(request.method()); if (!connection.isConnected()) { connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig()); if (connection.isSpdy()) client.getConnectionPool().share(connection); client.getRoutesDatabase().connected(connection.getRoute()); } else if (!connection.isSpdy()) { connection.updateReadTimeout(client.getReadTimeout()); } route = connection.getRoute();}
- 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
接下来我们要先看一下routeSelector.next()方法如何返回connection对象,然后在看Connection.connect()方法:
/*** Returns the next route address to attempt.** @throws NoSuchElementException if there are no more routes to attempt.*/public Connection next(String method) throws IOException { for (Connection pooled; (pooled = pool.get(address)) != null; ) { if (method.equals("GET") || pooled.isReadable()) return pooled; pooled.close(); } if (!hasNextTlsMode()) { if (!hasNextInetSocketAddress()) { if (!hasNextProxy()) { if (!hasNextPostponed()) { throw new NoSuchElementException(); } return new Connection(pool, nextPostponed()); } lastProxy = nextProxy(); resetNextInetSocketAddress(lastProxy); } lastInetSocketAddress = nextInetSocketAddress(); resetNextTlsMode(); } boolean modernTls = nextTlsMode() == TLS_MODE_MODERN; Route route = new Route(address, lastProxy, lastInetSocketAddress, modernTls); if (routeDatabase.shouldPostpone(route)) { postponedRoutes.add(route); return next(method); } return new Connection(pool, route);}
- 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
再看一下Connection
类的实现以及其connect()
方法:
public final class Connection implements Closeable { private final ConnectionPool pool; private final Route route; private Socket socket; private InputStream in; private OutputStream out; private BufferedSource source; private BufferedSink sink; private boolean connected = false; private HttpConnection httpConnection; private SpdyConnection spdyConnection; private int httpMinorVersion = 1; private long idleStartTimeNs; private Handshake handshake; private int recycleCount; public Connection(ConnectionPool pool, Route route) { this.pool = pool; this.route = route; } public void connect(int connectTimeout, int readTimeout, TunnelRequest tunnelRequest) throws IOException { if (connected) throw new IllegalStateException("already connected"); socket = (route.proxy.type() != Proxy.Type.HTTP) ? new Socket(route.proxy) : new Socket(); Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout); socket.setSoTimeout(readTimeout); in = socket.getInputStream(); out = socket.getOutputStream(); if (route.address.sslSocketFactory != null) { upgradeToTls(tunnelRequest); } else { initSourceAndSink(); httpConnection = new HttpConnection(pool, this, source, sink); } connected = true; } /** Returns the transport appropriate for this connection. */ public Object newTransport(HttpEngine httpEngine) throws IOException { return (spdyConnection != null) ? new SpdyTransport(httpEngine, spdyConnection) : new HttpTransport(httpEngine, httpConnection); }}````到这里就已经把发送请求到服务器的部分全部分析完了,就想上面所说的我们应该回去了,回去分析发送完请求后的部分。我们这个分析是在`HttpURLConnectionImpl.execute()`方法中的`HttpEngine.sendRequest()`方法开始一直分析下来的,所以我们还是要回到`HttpURLConnectionImpl.execute()`方法中. "se-preview-section-delimiter">```javaprivate boolean execute(boolean readResponse) throws IOException {try { httpEngine.sendRequest(); route = httpEngine.getRoute(); handshake = httpEngine.getConnection() != null ? httpEngine.getConnection().getHandshake() : null; if (readResponse) { httpEngine.readResponse(); } return true;} catch (IOException e) { HttpEngine retryEngine = httpEngine.recover(e); if (retryEngine != null) { httpEngine = retryEngine; return false; } httpEngineFailure = e; throw e;}}
- 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
接着看HttpEngine.readResponse()
方法吧。注释说的非常明白。
/*** Flushes the remaining request header and body, parses the HTTP response* headers and starts reading the HTTP response body if it exists.*/public final void readResponse() throws IOException {if (response != null) return;if (responseSource == null) throw new IllegalStateException("call sendRequest() first!");if (!responseSource.requiresConnection()) return;if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) { bufferedRequestBody.flush();}if (sentRequestMillis == -1) { if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) { long contentLength = ((RetryableSink) requestBodyOut).contentLength(); request = request.newBuilder() .header("Content-Length", Long.toString(contentLength)) .build(); } transport.writeRequestHeaders(request);}if (requestBodyOut != null) { if (bufferedRequestBody != null) { bufferedRequestBody.close(); } else { requestBodyOut.close(); } if (requestBodyOut instanceof RetryableSink) { transport.writeRequestBody((RetryableSink) requestBodyOut); }}transport.flushRequest();response = transport.readResponseHeaders() .request(request) .handshake(connection.getHandshake()) .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis)) .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())) .setResponseSource(responseSource) .build();connection.setHttpMinorVersion(response.httpMinorVersion());receiveHeaders(response.headers());if (responseSource == ResponseSource.CONDITIONAL_CACHE) { if (validatingResponse.validate(response)) { transport.emptyTransferStream(); releaseConnection(); response = combine(validatingResponse, response); OkResponseCache responseCache = client.getOkResponseCache(); responseCache.trackConditionalCacheHit(); responseCache.update(validatingResponse, cacheableResponse()); if (validatingResponse.body() != null) { initContentStream(validatingResponse.body().source()); } return; } else { closeQuietly(validatingResponse.body()); }}if (!hasResponseBody()) { responseTransferSource = transport.getTransferStream(cacheRequest); responseBody = responseTransferSource; return;}maybeCache();initContentStream(transport.getTransferStream(cacheRequest));}
- 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
先看一下maybeCache()方法:
private void maybeCache() throws IOException {OkResponseCache responseCache = client.getOkResponseCache();if (responseCache == null) return;if (!CacheStrategy.isCacheable(response, request)) { responseCache.maybeRemove(request); return;}cacheRequest = responseCache.put(cacheableResponse());}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
再看一下initContentStream()
方法:
/*** Initialize the response content stream from the response transfer source.* These two sources are the same unless we're doing transparent gzip, in* which case the content source is decompressed.** Whenever we do transparent gzip we also strip the corresponding headers.* We strip the Content-Encoding header to prevent the application from* attempting to double decompress. We strip the Content-Length header because* it is the length of the compressed content, but the application is only* interested in the length of the uncompressed content.**
This method should only be used for non-empty response bodies. Response* codes like "304 Not Modified" can include "Content-Encoding: gzip" without* a response body and we will crash if we attempt to decompress the zero-byte* source.*/
private void initContentStream(Source transferSource) throws IOException {responseTransferSource = transferSource;if (transparentGzip && "gzip".equalsIgnoreCase(response.header("Content-Encoding"))) { response = response.newBuilder() .removeHeader("Content-Encoding") .removeHeader("Content-Length") .build(); responseBody = new GzipSource(transferSource);} else { responseBody = transferSource;}}
- 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
到这里又执行完了,responseBody已经被赋值了,他是一个Source接口的实现类。也就是说到这里,这次网络请求就完成了,也收到了服务器返回的数据。
也就是说到这里我们已经分析了:
HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection();connection.connect();
接下来就是分析connection.getResponseCode()以及connection.getOutputStream()这两个方法了。
先看一下getResponseCode()方法:
@Override public final int getResponseCode() throws IOException {return getResponse().getResponse().code();}
那我们接着看一下getResponse()
方法,其实就是直接读取响应头的响应值:
/*** Aggressively tries to get the final HTTP response, potentially making* many HTTP requests in the process in order to cope with redirects and* authentication.*/private HttpEngine getResponse() throws IOException {initHttpEngine();if (httpEngine.hasResponse()) { return httpEngine;}while (true) { if (!execute(true)) { continue; } Retry retry = processResponseHeaders(); if (retry == Retry.NONE) { httpEngine.releaseConnection(); return httpEngine; } String retryMethod = method; Sink requestBody = httpEngine.getRequestBody(); int responseCode = httpEngine.getResponse().code(); if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM || responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) { retryMethod = "GET"; requestHeaders.removeAll("Content-Length"); requestBody = null; } if (requestBody != null && !(requestBody instanceof RetryableSink)) { throw new HttpRetryException("Cannot retry streamed HTTP body", responseCode); } if (retry == Retry.DIFFERENT_CONNECTION) { httpEngine.releaseConnection(); } Connection connection = httpEngine.close(); httpEngine = newHttpEngine(retryMethod, connection, (RetryableSink) requestBody);}}
- 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
接着看一下getOutputStream()
的源码:
@Override public final OutputStream getOutputStream() throws IOException {connect(); BufferedSink sink = httpEngine.getBufferedRequestBody();if (sink == null) { throw new ProtocolException("method does not support a request body: " + method);} else if (httpEngine.hasResponse()) { throw new ProtocolException("cannot write request body after response has been read");}return sink.outputStream();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
顺便再看一下getInputStream()
方法:
@Override public final InputStream getInputStream() throws IOException {if (!doInput) { throw new ProtocolException("This protocol does not support input");}HttpEngine response = getResponse();if (getResponseCode() >= HTTP_BAD_REQUEST) { throw new FileNotFoundException(url.toString());}InputStream result = response.getResponseBodyBytes();if (result == null) { throw new ProtocolException("No response body exists; responseCode=" + getResponseCode());}return result;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
再顺便看一下HttpURLConnection.disconnect()
方法,因为这个方法可能很多人不清楚该不该调用他,不过注释说的很明白了:
/** * Releases this connection so that its resources may be either reused or * closed. * * Unlike other Java implementations, this will not necessarily close * socket connections that can be reused. You can disable all connection * reuse by setting the {@code http.keepAlive} system property to {@code * false} before issuing any HTTP requests. */
@Override public final void disconnect() {if (httpEngine != null) { httpEngine.close();}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
看一下HttpEngine.close()方法:
/*** Release any resources held by this engine. If a connection is still held by* this engine, it is returned.*/public final Connection close() {if (bufferedRequestBody != null) { closeQuietly(bufferedRequestBody);} else if (requestBodyOut != null) { closeQuietly(requestBodyOut);}if (responseBody == null) { closeQuietly(connection); connection = null; return null;}closeQuietly(responseBody);closeQuietly(responseBodyBytes);if (transport != null && !transport.canReuseConnection()) { closeQuietly(connection); connection = null; return null;}Connection result = connection;connection = null;return result;}
- 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
继续看一下closeQuietly(connection)方法:
/*** Closes {@code closeable}, ignoring any checked exceptions. Does nothing* if {@code closeable} is null.*/public static void closeQuietly(Closeable closeable) {if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { }}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
再接着看一下Connection.close()方法:
@Override public void close() throws IOException {socket.close();}
再看Socket.close()方法:
package com.squareup.okhttp;/** * Closes the socket. It is not possible to reconnect or rebind to this * socket thereafter which means a new socket instance has to be created. * * @throws IOException * if an error occurs while closing the socket. */public synchronized void close() throws IOException { isClosed = true; isConnected = false; localAddress = Inet4Address.ANY; impl.close();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
到这里就都看完了,最后我们再看一下上面用到的连接池,也就是ConnectionPool类: 因为在上面的分析中,我们发现此类贯穿了很多类。
它为OkHttpClient中的对象,后贯穿到HttpEngine、Connection、HttpConnection等。所以要分析下。
/** * Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP * requests that share the same {@link com.squareup.okhttp.Address} may share a * {@link com.squareup.okhttp.Connection}. This class implements the policy of * which connections to keep open for future use. * * The {@link #getDefault() system-wide default} uses system properties for * tuning parameters: *
* - {@code http.keepAlive} true if HTTP and SPDY connections should be * pooled at all. Default is true. *
- {@code http.maxConnections} maximum number of idle connections to * each to keep in the pool. Default is 5. *
- {@code http.keepAliveDuration} Time in milliseconds to keep the * connection alive in the pool before closing it. Default is 5 minutes. * This property isn't used by {@code HttpURLConnection}. *
* * The default instance doesn't adjust its configuration as system * properties are changed. This assumes that the applications that set these * parameters do so before making HTTP connections, and that this class is * initialized lazily. */
public class ConnectionPool { private static final int MAX_CONNECTIONS_TO_CLEANUP = 2; private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000; private static final ConnectionPool systemDefault; static { String keepAlive = System.getProperty("http.keepAlive"); String keepAliveDuration = System.getProperty("http.keepAliveDuration"); String maxIdleConnections = System.getProperty("http.maxConnections"); long keepAliveDurationMs = keepAliveDuration != null ? Long.parseLong(keepAliveDuration) : DEFAULT_KEEP_ALIVE_DURATION_MS; if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) { systemDefault = new ConnectionPool(0, keepAliveDurationMs); } else if (maxIdleConnections != null) { systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs); } else { systemDefault = new ConnectionPool(5, keepAliveDurationMs); } } /** The maximum number of idle connections for each address. */ private final int maxIdleConnections; private final long keepAliveDurationNs; private final LinkedList connections = new LinkedList(); /** We use a single background thread to cleanup expired connections. */ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(), Util.threadFactory("OkHttp ConnectionPool", true)); private final Runnable connectionsCleanupRunnable = new Runnable() { @Override public void run() { List expiredConnections = new ArrayList(MAX_CONNECTIONS_TO_CLEANUP); int idleConnectionCount = 0; synchronized (ConnectionPool.this) { for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.isAlive() || connection.isExpired(keepAliveDurationNs)) { i.remove(); expiredConnections.add(connection); if (expiredConnections.size() == MAX_CONNECTIONS_TO_CLEANUP) break; } else if (connection.isIdle()) { idleConnectionCount++; } } for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious() && idleConnectionCount > maxIdleConnections; ) { Connection connection = i.previous(); if (connection.isIdle()) { expiredConnections.add(connection); i.remove(); --idleConnectionCount; } } } for (Connection expiredConnection : expiredConnections) { Util.closeQuietly(expiredConnection); } } }; public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) { this.maxIdleConnections = maxIdleConnections; this.keepAliveDurationNs = keepAliveDurationMs * 1000 * 1000; } /** * Returns a snapshot of the connections in this pool, ordered from newest to * oldest. Waits for the cleanup callable to run if it is currently scheduled. */ List getConnections() { waitForCleanupCallableToRun(); synchronized (this) { return new ArrayList(connections); } } /** * Blocks until the executor service has processed all currently enqueued * jobs. */ private void waitForCleanupCallableToRun() { try { executorService.submit(new Runnable() { @Override public void run() { } }).get(); } catch (Exception e) { throw new AssertionError(); } } public static ConnectionPool getDefault() { return systemDefault; } /** Returns total number of connections in the pool. */ public synchronized int getConnectionCount() { return connections.size(); } /** Returns total number of spdy connections in the pool. */ public synchronized int getSpdyConnectionCount() { int total = 0; for (Connection connection : connections) { if (connection.isSpdy()) total++; } return total; } /** Returns total number of http connections in the pool. */ public synchronized int getHttpConnectionCount() { int total = 0; for (Connection connection : connections) { if (!connection.isSpdy()) total++; } return total; } /** Returns a recycled connection to {@code address}, or null if no such connection exists. */ public synchronized Connection get(Address address) { Connection foundConnection = null; for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.getRoute().getAddress().equals(address) || !connection.isAlive() || System.nanoTime() - connection.getIdleStartTimeNs() >= keepAliveDurationNs) { continue; } i.remove(); if (!connection.isSpdy()) { try { Platform.get().tagSocket(connection.getSocket()); } catch (SocketException e) { Util.closeQuietly(connection); Platform.get().logW("Unable to tagSocket(): " + e); continue; } } foundConnection = connection; break; } if (foundConnection != null && foundConnection.isSpdy()) { connections.addFirst(foundConnection); } executorService.execute(connectionsCleanupRunnable); return foundConnection; } /** * Gives {@code connection} to the pool. The pool may store the connection, * or close it, as its policy describes. * * It is an error to use {@code connection} after calling this method. */
public void recycle(Connection connection) { if (connection.isSpdy()) { return; } if (!connection.isAlive()) { Util.closeQuietly(connection); return; } try { Platform.get().untagSocket(connection.getSocket()); } catch (SocketException e) { Platform.get().logW("Unable to untagSocket(): " + e); Util.closeQuietly(connection); return; } synchronized (this) { connections.addFirst(connection); connection.incrementRecycleCount(); connection.resetIdleStartTime(); } executorService.execute(connectionsCleanupRunnable); } /** * Shares the SPDY connection with the pool. Callers to this method may * continue to use {@code connection}. */ public void share(Connection connection) { if (!connection.isSpdy()) throw new IllegalArgumentException(); executorService.execute(connectionsCleanupRunnable); if (connection.isAlive()) { synchronized (this) { connections.addFirst(connection); } } } /** Close and remove all connections in the pool. */ public void evictAll() { List connections; synchronized (this) { connections = new ArrayList(this.connections); this.connections.clear(); } for (int i = 0, size = connections.size(); i < size; i++) { Util.closeQuietly(connections.get(i)); } }}
- 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
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
/** * Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP * requests that share the same {@link com.squareup.okhttp.Address} may share a * {@link com.squareup.okhttp.Connection}. This class implements the policy of * which connections to keep open for future use. * * The {@link #getDefault() system-wide default} uses system properties for * tuning parameters: *
* - {@code http.keepAlive} true if HTTP and SPDY connections should be * pooled at all. Default is true. *
- {@code http.maxConnections} maximum number of idle connections to * each to keep in the pool. Default is 5. *
- {@code http.keepAliveDuration} Time in milliseconds to keep the * connection alive in the pool before closing it. Default is 5 minutes. * This property isn't used by {@code HttpURLConnection}. *
* * The default instance doesn't adjust its configuration as system * properties are changed. This assumes that the applications that set these * parameters do so before making HTTP connections, and that this class is * initialized lazily. */
public class ConnectionPool { private static final int MAX_CONNECTIONS_TO_CLEANUP = 2; private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000; private static final ConnectionPool systemDefault; static { String keepAlive = System.getProperty("http.keepAlive"); String keepAliveDuration = System.getProperty("http.keepAliveDuration"); String maxIdleConnections = System.getProperty("http.maxConnections"); long keepAliveDurationMs = keepAliveDuration != null ? Long.parseLong(keepAliveDuration) : DEFAULT_KEEP_ALIVE_DURATION_MS; if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) { systemDefault = new ConnectionPool(0, keepAliveDurationMs); } else if (maxIdleConnections != null) { systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs); } else { systemDefault = new ConnectionPool(5, keepAliveDurationMs); } } /** The maximum number of idle connections for each address. */ private final int maxIdleConnections; private final long keepAliveDurationNs; private final LinkedList connections = new LinkedList(); /** We use a single background thread to cleanup expired connections. */ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(), Util.threadFactory("OkHttp ConnectionPool", true)); private final Runnable connectionsCleanupRunnable = new Runnable() { @Override public void run() { List expiredConnections = new ArrayList(MAX_CONNECTIONS_TO_CLEANUP); int idleConnectionCount = 0; synchronized (ConnectionPool.this) { for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.isAlive() || connection.isExpired(keepAliveDurationNs)) { i.remove(); expiredConnections.add(connection); if (expiredConnections.size() == MAX_CONNECTIONS_TO_CLEANUP) break; } else if (connection.isIdle()) { idleConnectionCount++; } } for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious() && idleConnectionCount > maxIdleConnections; ) { Connection connection = i.previous(); if (connection.isIdle()) { expiredConnections.add(connection); i.remove(); --idleConnectionCount; } } } for (Connection expiredConnection : expiredConnections) { Util.closeQuietly(expiredConnection); } } }; public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) { this.maxIdleConnections = maxIdleConnections; this.keepAliveDurationNs = keepAliveDurationMs * 1000 * 1000; } /** * Returns a snapshot of the connections in this pool, ordered from newest to * oldest. Waits for the cleanup callable to run if it is currently scheduled. */ List getConnections() { waitForCleanupCallableToRun(); synchronized (this) { return new ArrayList(connections); } } /** * Blocks until the executor service has processed all currently enqueued * jobs. */ private void waitForCleanupCallableToRun() { try { executorService.submit(new Runnable() { @Override public void run() { } }).get(); } catch (Exception e) { throw new AssertionError(); } } public static ConnectionPool getDefault() { return systemDefault; } /** Returns total number of connections in the pool. */ public synchronized int getConnectionCount() { return connections.size(); } /** Returns total number of spdy connections in the pool. */ public synchronized int getSpdyConnectionCount() { int total = 0; for (Connection connection : connections) { if (connection.isSpdy()) total++; } return total; } /** Returns total number of http connections in the pool. */ public synchronized int getHttpConnectionCount() { int total = 0; for (Connection connection : connections) { if (!connection.isSpdy()) total++; } return total; } /** Returns a recycled connection to {@code address}, or null if no such connection exists. */ public synchronized Connection get(Address address) { Connection foundConnection = null; for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.getRoute().getAddress().equals(address) || !connection.isAlive() || System.nanoTime() - connection.getIdleStartTimeNs() >= keepAliveDurationNs) { continue; } i.remove(); if (!connection.isSpdy()) { try { Platform.get().tagSocket(connection.getSocket()); } catch (SocketException e) { Util.closeQuietly(connection); Platform.get().logW("Unable to tagSocket(): " + e); continue; } } foundConnection = connection; break; } if (foundConnection != null && foundConnection.isSpdy()) { connections.addFirst(foundConnection); } executorService.execute(connectionsCleanupRunnable); return foundConnection; } /** * Gives {@code connection} to the pool. The pool may store the connection, * or close it, as its policy describes. * * It is an error to use {@code connection} after calling this method. */
public void recycle(Connection connection) { if (connection.isSpdy()) { return; } if (!connection.isAlive()) { Util.closeQuietly(connection); return; } try { Platform.get().untagSocket(connection.getSocket()); } catch (SocketException e) { Platform.get().logW("Unable to untagSocket(): " + e); Util.closeQuietly(connection); return; } synchronized (this) { connections.addFirst(connection); connection.incrementRecycleCount(); connection.resetIdleStartTime(); } executorService.execute(connectionsCleanupRunnable); } /** * Shares the SPDY connection with the pool. Callers to this method may * continue to use {@code connection}. */ public void share(Connection connection) { if (!connection.isSpdy()) throw new IllegalArgumentException(); executorService.execute(connectionsCleanupRunnable); if (connection.isAlive()) { synchronized (this) { connections.addFirst(connection); } } } /** Close and remove all connections in the pool. */ public void evictAll() { List connections; synchronized (this) { connections = new ArrayList(this.connections); this.connections.clear(); } for (int i = 0, size = connections.size(); i < size; i++) { Util.closeQuietly(connections.get(i)); } }}
- 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
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
文章比较长。。看了一天,就一口气写完了,希望能看到这里。
- Android(安卓)添加程序到桌面favorites列表
- Android——Jni使用总结
- Android(安卓)常见的几种加密方式RSA、CBC的集成
- android夜间模式的实现
- OkHttpClientManager使用
- Android(java)学习笔记125:Clock app编写报错02
- android 百度地图定位功能实现
- android线程间通信和主线程更新ui
- Android中使用OrmLite来对SQLite数据库进行CRUD(增删改查)解析
随机推荐
-
复习android SQLiteOpenHelper
-
Android关闭输入法
-
Android 获取当前进程
-
android隐藏底部虚拟键Navigation Bar实
-
android 中遇到的空指针异常
-
Android中设置146种颜色相对应的xml值
-
Android(安卓)序列化对象接口Parcelable
-
android之SeekBar控件用法
-
Android Hal 分析
-
android识别 单击和双击事件