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

Posted 火山石

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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<

以上是关于Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 4.4以上的存储读写权限

Android 4.4 以上更改状态栏颜色

如何检查我是否在Android 4.4及以上版本中以编程方式打开gps? [重复]

GetPathFromUri4kitkatAndroid 4.4 kitkat以上及以下根据uri获取路径的方法

android开发 我新建了一个项目兼容2.1到4.4 会出现appcompat_v7文件

S905L(P211)盒子刷android tv以及刷emuelec 4.4/4.5的向导/方法