Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析

研究了一下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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

接下来就要看一下streamHandler究竟是何方神圣?我们搜一下他的赋值,实在setupStreamHandler方法中进行的:

/**
 * Sets the receiver's stream handler to one which is appropriate for its
 * protocol.
 *
 * <p>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() {
    // Check for a cached (previously looked up) handler for
    // the requested protocol.
    streamHandler = streamHandlers.get(protocol);
    if (streamHandler != null) {
        return;
    }

    // If there is a stream handler factory, then attempt to
    // use it to create the handler.
    if (streamHandlerFactory != null) {
        streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
        if (streamHandler != null) {
            streamHandlers.put(protocol, streamHandler);
            return;
        }
    }

    // Check if there is a list of packages which can provide handlers.
    // If so, then walk this list looking for an applicable one.
    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) {
            }
        }
    }

    // Fall back to a built-in stream handler if the user didn't supply one
    if (protocol.equals("file")) {
        streamHandler = new FileHandler();
    } else if (protocol.equals("ftp")) {
        streamHandler = new FtpHandler();
    } else if (protocol.equals("http")) {
        // 判断一下如果是HTTP协议,就会创建HtppHandler。看到这里明白了,原来使用的是okhttp.
        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()方法:

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
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 {
        // 调用了OKHttpClient()的方法
        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的时候该方法就已经已经过时了。

@Deprecated
public HttpURLConnection open(URL url)
Deprecated. moved to OkUrlFactory.open.
  • 1
  • 2
  • 3

那我们怎么往下分析呢?很显然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<Protocol> 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.
   *
   * <p>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.
   *
   * <p>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.
   *
   * <p>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.
   *
   * <p>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.
   *
   * <p>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.
   *
   * <p>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.
   *
   * <p>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<String> transports) {
    List<Protocol> protocols = new ArrayList<Protocol>(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.
   *
   * <p>The following protocols are currently supported:
   * <ul>
   *   <li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http/1.1</a>
   *   <li><a href="http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1">spdy/3.1</a>
   *   <li><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09">HTTP-draft-09/2.0</a>
   * </ul>
   *
   * <p><strong>This is an evolving set.</strong> 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.
   *
   * <p>If multiple protocols are specified, <a
   * href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> will
   * be used to negotiate a transport. Future releases may use another mechanism
   * (such as <a href="http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02">ALPN</a>)
   * 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<Protocol> 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<String> getTransports() {
    List<String> transports = new ArrayList<String>(protocols.size());
    for (int i = 0, size = protocols.size(); i < size; i++) {
      transports.add(protocols.get(i).name.utf8());
    }
    return transports;
  }

  public List<Protocol> getProtocols() {
    return protocols;
  }

  public HttpURLConnection open(URL url) {
    return open(url, proxy);
  }

  HttpURLConnection open(URL url, Proxy proxy) {
    String protocol = url.getProtocol();
    // 将该对象clone后设置一些其他的属性返回,里面会设置一个默认的连接池。
    OkHttpClient copy = copyWithDefaults();
    copy.proxy = proxy;
    // 返回了HttpURLConnectionImpl,并且把clone后的OKHttpClient对象传递进去。
    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) {
      // 会给OkHttpClient设置一个默认的连接池
      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.
   * <p>
   * 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(); // The system has no TLS. Just give up.
      }
    }
    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}.
   *
   * <p>This code configures OkHttp to handle all HTTP and HTTPS connections
   * created with {@link URL#openConnection()}: <pre>   {@code
   *
   *   OkHttpClient okHttpClient = new OkHttpClient();
   *   URL.setURLStreamHandlerFactory(okHttpClient);
   * }</pre>
   */
  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 {
    .....
}
  • 1
  • 2
  • 3

到这里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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

接着看一下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")) {
          // they are requesting a stream to write to. This implies a POST method
          method = "POST";
        } else if (!HttpMethod.hasRequestBody(method)) {
          // If the request method is neither POST nor PUT nor PATCH, then you're not writing
          throw new ProtocolException(method + " does not support writing");
        }
      }

      // 将newHttpEngine方法的返回值赋值给HttpEngine的成员变量。
      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 /* No body; that's passed separately. */);
    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();

    // If we're currently not using caches, make sure the engine's client doesn't have one.
    OkHttpClient engineClient = client;
    if (engineClient.getOkResponseCache() != null && !getUseCaches()) {
      engineClient = client.clone().setOkResponseCache(null);
    }
    // 将之前通过构造函数传递进来的OkHttpClient对象clone一份后再传递给HttpEngine
    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方法。
  httpEngine.sendRequest();
  route = httpEngine.getRoute();
  handshake = httpEngine.getConnection() != null
      ? httpEngine.getConnection().getHandshake()
      : null;
  // 读取结果,我们先不分析这里,等把sendRequest部分全部分析完成后再回来分析readResponse()部分。
  if (readResponse) {
    httpEngine.readResponse();
  }

  return true;
} catch (IOException e) {
  HttpEngine retryEngine = httpEngine.recover(e);
  if (retryEngine != null) {
    httpEngine = retryEngine;
    return false;
  }

  // Give up; recovery is not possible.
  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:
 * <ol>
 * <li>It is created.
 * <li>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.
 * <li>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.
 * </ol>
 *
 * <p>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; // Already sent.
    if (transport != null) throw new IllegalStateException();

    // 设置一些请求头,正式这个方法内部默认设置了`Keep-Alive`的值,也就是在Android Level 9之前因为Bug,我们需要关闭它的具体处理位置。
    prepareRawRequestHeaders();
    // 处理cache
    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()); // We don't need this cached response. Close it.
    }

    if (responseSource.requiresConnection()) {
      // Open a connection unless we inherited one from a redirect.
      if (connection == null) {
        // 调用connect方法,内部会重新创建一个connection,连接到服务器、重定向或者通过代理。
        connect();
      }
      // 通过Connection创建一个HttpTransport类。这个和后面的connection类一起看, Transport接口提供了一个用户写Request头和数据的输出流。
      transport = (Transport) connection.newTransport(this);

      // Create a request body if we don't have one already. We'll already have
      // one if we're retrying a failed POST.
      if (hasRequestBody() && requestBodyOut == null) {
        // 通过transport创建一个请求体的输出流,requestBodyOut是Sink接口的实现类,其实就是将请求头和请求体发送给服务器。这部分跟下去内容比较多,就不往下跟了。
        // 到这里就已经完成了与服务器的连接功能,并且把请求内容发送给服务器。请求部分就执行完了,可以回去了,还知道开头是哪吗?哈哈。接下来的就是从服务器接口读取返回数据了。
        requestBodyOut = transport.createRequestBody(request);
      }

    } else {
      // We're using a cached response. Recycle a connection we may have inherited from a redirect.
      if (connection != null) {
        // 回收connection,这里就看到了连接池,这个client就是构造函数中传递进来的OKHttpClient
        client.getConnectionPool().recycle(connection);
        connection = null;
      }

      // No need for the network! Promote the cached response immediately.
      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());
      // RoteSeclector类介绍. Selects routes to connect to an origin server. Each connection requires a
      // choice of proxy server, IP address, and TLS mode. Connections may also be
      // recycled.注意他把OkHttpClient中的connection pool传递进来了。
      routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(),
          client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
    }

    // roteSeclecrot.next()方法的注释Returns the next route address to attempt.这一步非常重要。
    connection = routeSelector.next(request.method());

    if (!connection.isConnected()) {
      // connection 进行连接了啊。他里面会用Socket开始连了。。后面我们再细看。简单的说Connection管理了Socket,后面我们要重点看这个类。
      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 {
    // 使用连接池获取Connection的地方。pool就是OkHttpClient中的连接池。
    // Always prefer pooled connections over new connections.
    for (Connection pooled; (pooled = pool.get(address)) != null; ) {
      // 匹配get方法,或者判断是否可读,http1.x是通过判断socket是否关闭来判断是否可读的。
      if (method.equals("GET") || pooled.isReadable()) return pooled;
      // 不满足重用,就关闭。
      pooled.close();
    }

    // Compute the next route to attempt.
    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);
      // We will only recurse in order to skip previously failed routes. They will be
      // tried last.
      return next(method);
    }
    // 没有的话也会去创建,并把OkHttpClient中的连接池传递进去。
    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; // Assume HTTP/1.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();
    // 连socket了,内部调用了socket.connect()方法。Connects this socket to the given remote host address and port specified
    // by the SocketAddress {@code remoteAddr} with the specified timeout. The
    // connecting method will block until the connection is established or an
    // error occurred.
    Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout);
    socket.setSoTimeout(readTimeout);
    in = socket.getInputStream();
    out = socket.getOutputStream();

    if (route.address.sslSocketFactory != null) {
      // 完成TLS握手和验证
      upgradeToTls(tunnelRequest);
    } else {
      initSourceAndSink();
      // 创建HttpConnection.A socket connection that can be used to send HTTP/1.1 messages.
      // 这个HttpConnection有什么用呢?就是下面的newTransport方法中会用。而且还要把连接池传递进去?
      httpConnection = new HttpConnection(pool, this, source, sink);
    }
    // 这样就已经连接上了
    connected = true;
  }

  // 该方法决定了使用的协议是SPDY还是HTTP
  /** 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()`方法中. 

<div class="se-preview-section-delimiter"></div>

```java
private boolean execute(boolean readResponse) throws IOException {
try {
  // 上面已经把sendRequest部分全部分析完了,该方法会与服务器通过Socket建立连接并把请求部分发送给服务器。
  httpEngine.sendRequest();
  route = httpEngine.getRoute();
  handshake = httpEngine.getConnection() != null
      ? httpEngine.getConnection().getHandshake()
      : null;
  if (readResponse) {
    // 发送完请求之后该干什么呢? 当然是读取返回数据了。。。 没错就是它。但是不要忘了在connect()方法传递过来的时候这个值是false。
    // 所以这一步在这里是不执行的,但是我们也分析下,方便以后理解。那这个值什么时候是true呢?就是在getResponse()方法中。
    httpEngine.readResponse();
  }

  return true;
} catch (IOException e) {
  HttpEngine retryEngine = httpEngine.recover(e);
  if (retryEngine != null) {
    httpEngine = retryEngine;
    return false;
  }

  // Give up; recovery is not possible.
  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;

// Flush the request body if there's data outstanding.
if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
  bufferedRequestBody.flush();
}

if (sentRequestMillis == -1) {
  if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) {
    // We might not learn the Content-Length until the request body has been buffered.
    long contentLength = ((RetryableSink) requestBodyOut).contentLength();
    request = request.newBuilder()
        .header("Content-Length", Long.toString(contentLength))
        .build();
  }
  transport.writeRequestHeaders(request);
}

if (requestBodyOut != null) {
  if (bufferedRequestBody != null) {
    // This also closes the wrapped requestBodyOut.
    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) {
  // 检查缓存是否可用,如果可用就用当前缓存的response。并且释放该连接。
  if (validatingResponse.validate(response)) {
    transport.emptyTransferStream();
    releaseConnection();
    response = combine(validatingResponse, response);

    // Update the cache after combining headers but before stripping the
    // Content-Encoding header (as performed by initContentStream()).
    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()) {
  // Don't call initContentStream() when the response doesn't have any content.
  responseTransferSource = transport.getTransferStream(cacheRequest);
  responseBody = responseTransferSource;
  return;
}

// 设置cacheRequest的值
maybeCache();
// 设置返回数据了,transport这里也就是HttpTransport。他的getTransferStream返回的是一个Source接口的实现类。也就是返回的数据。
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;

// Should we cache this response for this request?
if (!CacheStrategy.isCacheable(response, request)) {
  responseCache.maybeRemove(request);
  return;
}

// Offer this request to the cache.
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.
*
* <p>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.
*
* <p>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为null,有结果了就会给他赋值。
  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();
  • 1
  • 2

接下来就是分析connection.getResponseCode()以及connection.getOutputStream()这两个方法了。 

先看一下getResponseCode()方法:

@Override public final int getResponseCode() throws IOException {
// 看到了吗?这里就是刚才我们说的execute()方法的参数什么时候会为true的地方。
return getResponse().getResponse().code();
}
  • 1
  • 2
  • 3
  • 4

那我们接着看一下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) {
  // 参数为true了。
  if (!execute(true)) {
    continue;
  }

  Retry retry = processResponseHeaders();
  if (retry == Retry.NONE) {
    httpEngine.releaseConnection();
    return httpEngine;
  }

  // The first request was insufficient. Prepare for another...
  String retryMethod = method;
  Sink requestBody = httpEngine.getRequestBody();

  // Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
  // redirect should keep the same method, Chrome, Firefox and the
  // RI all issue GETs when following any redirect.
  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()方法
connect();

   // 这里可能有人会有说,getOutputStream和request body有什么关系,应该是response body才对啊。
// 不要弄混了啊,getOutputStream是要把post请求的数据输入给请求。
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");
}
// 它也会直接调用getResponse()方法,这个比较好理解。
HttpEngine response = getResponse();

// if the requested file does not exist, throw an exception formerly the
// Error page from the server was returned if the requested file was
// text/html this has changed to return FileNotFoundException for all
// file types
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.
 *
 * <p>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() {
// Calling disconnect() before a connection exists should have no effect.
if (httpEngine != null) {
  // 调用HttpEngine.close()方法
  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) {
  // This also closes the wrapped requestBodyOut.
  closeQuietly(bufferedRequestBody);
} else if (requestBodyOut != null) {
  closeQuietly(requestBodyOut);
}

// If this engine never achieved a response body, its connection cannot be reused.
if (responseBody == null) {
  closeQuietly(connection);
  connection = null;
  return null;
}

// Close the response body. This will recycle the connection if it is eligible.
closeQuietly(responseBody);

// Clear the buffer held by the response body input stream adapter.
closeQuietly(responseBodyBytes);

// HttpTransport.canReuseConnection()用于判断该Connection是否可复用
// Close the connection if it cannot be reused.
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 {
    // 这里也就是Connection的close()方法
    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也关了。
socket.close();
}
  • 1
  • 2
  • 3
  • 4

再看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;
    // RI compatibility: the RI returns the any address (but the original local port) after
    // close.
    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.
 *
 * <p>The {@link #getDefault() system-wide default} uses system properties for
 * tuning parameters:
 * <ul>
 *     <li>{@code http.keepAlive} true if HTTP and SPDY connections should be
 *         pooled at all. Default is true.
 *     <li>{@code http.maxConnections} maximum number of idle connections to
 *         each to keep in the pool. Default is 5.
 *     <li>{@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}.
 * </ul>
 *
 * <p>The default instance <i>doesn't</i> 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; // 5 min

  // 这个就是getDefault方法所返回的默认连接池。
  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<Connection> connections = new LinkedList<Connection>();

  /** We use a single background thread to cleanup expired connections. */
  private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
      60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
      Util.threadFactory("OkHttp ConnectionPool", true));
  private final Runnable connectionsCleanupRunnable = new Runnable() {
    @Override public void run() {
      List<Connection> expiredConnections = new ArrayList<Connection>(MAX_CONNECTIONS_TO_CLEANUP);
      int idleConnectionCount = 0;
      synchronized (ConnectionPool.this) {
        for (ListIterator<Connection> 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<Connection> 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<Connection> getConnections() {
    waitForCleanupCallableToRun();
    synchronized (this) {
      return new ArrayList<Connection>(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<Connection> 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()) {
        // 不是spdy连接
        try {
          // Platforml类对当前Android平台做了适配。
          Platform.get().tagSocket(connection.getSocket());
        } catch (SocketException e) {
          Util.closeQuietly(connection);
          // When unable to tag, skip recycling and close
          Platform.get().logW("Unable to tagSocket(): " + e);
          continue;
        }
      }
      // 找到可复用的Connection
      foundConnection = connection;
      break;
    }

    // 针对spdy连接,添加到连接池中
    if (foundConnection != null && foundConnection.isSpdy()) {
      connections.addFirst(foundConnection); // Add it back after iteration.
    }

    executorService.execute(connectionsCleanupRunnable);
    return foundConnection;
  }

  /**
   * Gives {@code connection} to the pool. The pool may store the connection,
   * or close it, as its policy describes.
   *
   * <p>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) {
      // When unable to remove tagging, skip recycling and close.
      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<Connection> connections;
    synchronized (this) {
      connections = new ArrayList<Connection>(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.
 *
 * <p>The {@link #getDefault() system-wide default} uses system properties for
 * tuning parameters:
 * <ul>
 *     <li>{@code http.keepAlive} true if HTTP and SPDY connections should be
 *         pooled at all. Default is true.
 *     <li>{@code http.maxConnections} maximum number of idle connections to
 *         each to keep in the pool. Default is 5.
 *     <li>{@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}.
 * </ul>
 *
 * <p>The default instance <i>doesn't</i> 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; // 5 min

  // 这个就是getDefault方法所返回的默认连接池。
  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<Connection> connections = new LinkedList<Connection>();

  /** We use a single background thread to cleanup expired connections. */
  private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
      60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
      Util.threadFactory("OkHttp ConnectionPool", true));
  private final Runnable connectionsCleanupRunnable = new Runnable() {
    @Override public void run() {
      List<Connection> expiredConnections = new ArrayList<Connection>(MAX_CONNECTIONS_TO_CLEANUP);
      int idleConnectionCount = 0;
      synchronized (ConnectionPool.this) {
        for (ListIterator<Connection> 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<Connection> 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<Connection> getConnections() {
    waitForCleanupCallableToRun();
    synchronized (this) {
      return new ArrayList<Connection>(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<Connection> 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()) {
        // 不是spdy连接
        try {
          // Platforml类对当前Android平台做了适配。
          Platform.get().tagSocket(connection.getSocket());
        } catch (SocketException e) {
          Util.closeQuietly(connection);
          // When unable to tag, skip recycling and close
          Platform.get().logW("Unable to tagSocket(): " + e);
          continue;
        }
      }
      // 找到可复用的Connection
      foundConnection = connection;
      break;
    }

    // 针对spdy连接,添加到连接池中
    if (foundConnection != null && foundConnection.isSpdy()) {
      connections.addFirst(foundConnection); // Add it back after iteration.
    }

    executorService.execute(connectionsCleanupRunnable);
    return foundConnection;
  }

  /**
   * Gives {@code connection} to the pool. The pool may store the connection,
   * or close it, as its policy describes.
   *
   * <p>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) {
      // When unable to remove tagging, skip recycling and close.
      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<Connection> connections;
    synchronized (this) {
      connections = new ArrayList<Connection>(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

文章比较长。。看了一天,就一口气写完了,希望能看到这里。

上一篇:Memcached(一)在Windows上安装和测试memcached


下一篇:linux环境下安装redis扩展