人家说不懂OKHttp源码的安卓开发程序员不算好的安卓开发程序员那我们就分析下okhttp这货到底在源码层做了什么吧。
首先我们来看okhttp的核心类OkHttpClient,我们会发现整个的OkHttpClient只有200行左右的代码是在处理自己的逻辑大部分工作都放在了自己的一个内部类Builder 上面下面就是让人头大的Builder设置首先上源代码然后我们在一点一点分析,源码如下。
public static final class Builder {
Dispatcher dispatcher;
Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
TrustRootIndex trustRootIndex;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.trustRootIndex = okHttpClient.trustRootIndex;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
}
/**
* 设置新连接的默认连接超时。 值0表示没有超时,否则值转换为毫秒时必须在1和{@link整数#MAX_VALUE}之间。
*/
public Builder connectTimeout(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.");
if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
connectTimeout = (int) millis;
return this;
}
/**
* 设置新连接的默认读取超时。 值0表示没有超时,否则值转换为毫秒时必须在1和{@link整数#MAX_VALUE}之间。
*/
public Builder readTimeout(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.");
if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
readTimeout = (int) millis;
return this;
}
/**
*设置新连接的默认写入超时。 值0表示没有超时,否则值转换为毫秒时必须在1和{@link整数#MAX_VALUE}之间。
*/
public Builder writeTimeout(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.");
if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
writeTimeout = (int) millis;
return this;
}
/**
* 设置此客户端创建的连接将使用的HTTP代理。这优先于{@link #proxySelector},只有在此代理为null(默认情况下为null)时才会生效。 要完全禁用代理使用,请调用{@code setProxy(Proxy.NO_PROXY)}。
*/
public Builder proxy(Proxy proxy) {
this.proxy = proxy;
return this;
}
/**
* 如果未明确指定{@link #proxy proxy},请设置要使用的代理选择策略。 代理选择器可以返回多个代理; 在这种情况下,将按顺序尝试它们,直到建立成功的连接。
* <p>如果未设置,将使用{@link ProxySelector#getDefault()系统范围默认值)代理选择器。
*/
public Builder proxySelector(ProxySelector proxySelector) {
this.proxySelector = proxySelector;
return this;
}
/**
* 设置可以接受来自传入HTTP响应的Cookie并向传出的HTTP请求提供Cookie的处理程序。
* <p>如果未设置,则将接受{@ linkplain CookieJar#NO_COOKIES无Cookie}。
*/
public Builder cookieJar(CookieJar cookieJar) {
if (cookieJar == null) throw new NullPointerException("cookieJar == null");
this.cookieJar = cookieJar;
return this;
}
/** 设置用于读取和写入缓存响应的响应缓存。*/
void setInternalCache(InternalCache internalCache) {
this.internalCache = internalCache;
this.cache = null;
}
public Builder cache(Cache cache) {
this.cache = cache;
this.internalCache = null;
return this;
}
/**
* 设置用于查找主机名的IP地址的DNS服务。
*
* <p>如果未设置,将使用{@link Dns#SYSTEM系统范围的默认值} DNS。
*/
public Builder dns(Dns dns) {
if (dns == null) throw new NullPointerException("dns == null");
this.dns = dns;
return this;
}
/**
*设置用于创建连接的套接字工厂。 OkHttp只使用无参数的{@link SocketFactory#createSocket()createSocket()}方法创建不连接的套接字。 覆盖此方法,e。 例如,允许套接字绑定到特定的本地地址。
* <p>如果取消设置,将使用{@link SocketFactory#getDefault()系统级默认值}套接字工厂。
*/
public Builder socketFactory(SocketFactory socketFactory) {
if (socketFactory == null) throw new NullPointerException("socketFactory == null");
this.socketFactory = socketFactory;
return this;
}
/**
* 设置用于保护HTTPS连接的套接字工厂。
* <p>如果取消设置,将使用一个延迟创建的SSL套接字工厂。
*/
public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
this.sslSocketFactory = sslSocketFactory;
this.trustRootIndex = null;
return this;
}
/**
* 设置用于确认响应证书适用于HTTPS连接的请求主机名的验证器。
* <p>如果未设置,将使用默认主机名验证器。
*/
public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
if (hostnameVerifier == null) throw new NullPointerException("hostnameVerifier == null");
this.hostnameVerifier = hostnameVerifier;
return this;
}
/**
*设置约束哪些证书受信任的证书管理器。 默认情况下,HTTPS连接仅依靠{@link #sslSocketFactory SSL套接字工厂}建立信任。 固定证书避免了信任证书颁发机构的需要。
*/
public Builder certificatePinner(CertificatePinner certificatePinner) {
if (certificatePinner == null) throw new NullPointerException("certificatePinner == null");
this.certificatePinner = certificatePinner;
return this;
}
/**
* 设置用于响应来自源服务器的挑战的验证器。 使用{@link #proxyAuthenticator}为代理服务器设置身份验证者。
* <p>如果未设置,{@linkplain Authenticator#NONE将尝试不进行身份验证}。
*/
public Builder authenticator(Authenticator authenticator) {
if (authenticator == null) throw new NullPointerException("authenticator == null");
this.authenticator = authenticator;
return this;
}
/**
*设置用于响应来自代理服务器的挑战的验证器。 使用{@link #authenticator}为原始服务器设置验证器。
* <p>如果未设置,{@linkplain Authenticator#NONE将尝试不进行身份验证}。
*/
public Builder proxyAuthenticator(Authenticator proxyAuthenticator) {
if (proxyAuthenticator == null) throw new NullPointerException("proxyAuthenticator == null");
this.proxyAuthenticator = proxyAuthenticator;
return this;
}
/**
* 设置用于回收HTTP和HTTPS连接的连接池。
* 如果取消设置,将使用新的连接池。
*/
public Builder connectionPool(ConnectionPool connectionPool) {
if (connectionPool == null) throw new NullPointerException("connectionPool == null");
this.connectionPool = connectionPool;
return this;
}
/**
*配置此客户端以跟踪从HTTPS到HTTP以及从HTTP到HTTPS的重定向。
* <p>如果未设置,将遵循协议重定向。 这与内置的{@code HttpURLConnection}的默认值不同。
*/
public Builder followSslRedirects(boolean followProtocolRedirects) {
this.followSslRedirects = followProtocolRedirects;
return this;
}
/**配置此客户端以跟踪重定向。 如果未设置,将按照重定向。 */
public Builder followRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
/**
*配置此客户端以在遇到连接问题时重试或不重试。 默认情况下,此客户端静默恢复以下问题:
* 无法访问的IP地址 。如果网址的主机有多个IP地址, 无法访问任何单独的IP地址不会使整个请求失败。 这个可以 增加多宿主服务的可用性。 >陈旧的汇集连接 {@link ConnectionPool}重复使用套接字
*减少请求延迟,但这些连接会偶尔超时。 无法访问的代理服务器 。{@link ProxySelector}可用于 按顺序尝试多个代理服务器,最终回落到直接连接。
*将此设置为false,以避免重试请求,这样做是破坏性的。 在这种情况下,呼叫应用程序应该自己恢复连接故障。
*/
public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) {
this.retryOnConnectionFailure = retryOnConnectionFailure;
return this;
}
/**
*设置用于设置策略和执行异步请求的分派器。 不得为空。
*/
public Builder dispatcher(Dispatcher dispatcher) {
if (dispatcher == null) throw new IllegalArgumentException("dispatcher == null");
this.dispatcher = dispatcher;
return this;
}
/**
* 配置此客户端使用的协议与远程服务器通信。 默认情况下,这个客户端将更喜欢最有效的传输可用,回落到更多无处不在的协议。 应用程序应仅调用此方法以避免特定的兼容性问题,例如启用SPDY时行为不正确的Web服务器。
*
* <p>目前支持以下协议:
*
* <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-17">h2</a>
* </ul>
*
* <p><strong>这是一个不断变化的集合。</strong> 未来版本包括对过渡协议的支持。 http / 1.1传输永远不会被丢弃。
*
* <p>如果指定了多个协议 <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg">ALPN</a> 将用于谈判运输。
*
* <p>{@link Protocol#HTTP_1_0} 在此集合中不受支持。 请求以 {@code HTTP/1.1} only. 如果服务器响应 {@code HTTP/1.0}, 将被暴露 {@link Response#protocol()}.
*
* @param 协议使用的协议,按优先级顺序。 该列表必须包含{@link协议#HTTP_1_1}。 它不能包含null或{@link协议#HTTP_1_0}。
*/
public Builder protocols(List<Protocol> protocols) {
protocols = Util.immutableList(protocols);
if (!protocols.contains(Protocol.HTTP_1_1)) {
throw new IllegalArgumentException("protocols doesn't contain http/1.1: " + protocols);
}
if (protocols.contains(Protocol.HTTP_1_0)) {
throw new IllegalArgumentException("protocols must not contain http/1.0: " + protocols);
}
if (protocols.contains(null)) {
throw new IllegalArgumentException("protocols must not contain null");
}
this.protocols = Util.immutableList(protocols);
return this;
}
public Builder connectionSpecs(List<ConnectionSpec> connectionSpecs) {
this.connectionSpecs = Util.immutableList(connectionSpecs);
return this;
}
/**
*返回可观察到每个调用的完整范围的拦截器列表:从建立连接之前(如果有)直到选择响应源(原始服务器,缓存或两者)之后。
*/
public List<Interceptor> interceptors() {
return interceptors;
}
public Builder addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
return this;
}
/**
* 返回可观察到单个网络请求和响应的拦截器列表。 这些拦截器必须调用{@link Interceptor.Chain#proceed}一次,这是网络拦截器短接或重复网络请求的错误。
*/
public List<Interceptor> networkInterceptors() {
return networkInterceptors;
}
public Builder addNetworkInterceptor(Interceptor interceptor) {
networkInterceptors.add(interceptor);
return this;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
源码已经看完了的话我们就开始分析了首先分析第一个构造
public Builder() {
dispatcher = new Dispatcher();//okhttp的分发器
protocols = DEFAULT_PROTOCOLS;//HTTP支持的协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;//Connection中的一些常量和变量
proxySelector = ProxySelector.getDefault();//OKHttp的服务器代理设置
cookieJar = CookieJar.NO_COOKIES;//默认没有cookie
socketFactory = SocketFactory.getDefault();//创建socket的工厂
hostnameVerifier = OkHostnameVerifier.INSTANCE;//默认主机验证https使用
certificatePinner = CertificatePinner.DEFAULT;//默认证书校验https使用
proxyAuthenticator = Authenticator.NONE;//无证书验证https使用
authenticator = Authenticator.NONE;//无证书验证https使用
connectionPool = new ConnectionPool();//连接池
dns = Dns.SYSTEM;//okhttp的DNS用于解析域名(这是okHttp切换ip的关键设置)
followSslRedirects = true;//是否禁止okhttp的重定向https使用
followRedirects = true;//是否禁止okhttp的重定向
retryOnConnectionFailure = true;//是否在响应失败后重新发送请求
connectTimeout = 10_000;//连接超时设置
readTimeout = 10_000;//读取网络信息超时设置
writeTimeout = 10_000;//写入网络信息超时设置
}
上面源码也就是我们创造个无参数的构造时我们会初始化里面的内容初始化的内容在注释中已经介绍了这一大篇内容让我看的头晕眼花啊还是总结下吧
在默认构造中okhttp给我们创建了
1.创建了okhttp的分发器
2.创建了okhttp的连接属性(协议,连接常量,服务代理(我也不太明白),socket的工厂,DNS)
3.创建了okHttp的缓存设置(cookie,连接池)
4.创建了okHttp对于Https的支持(主机验证,证书校验,是否禁止重定向)
5.创建了okHttp默认的常量(是否禁止重定向,是否在响应失败后重新发送请求,超时设置)
估计你现在已经迷糊了我也迷糊了那我们就继续往下看
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.trustRootIndex = okHttpClient.trustRootIndex;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
}
瞬间亮了属性还是以前的属性人还是以前的人只不过是从okHttpClient里面获取的那我就不介绍它了咱们继续往下看。
/**
* 设置新连接的默认连接超时。 值0表示没有超时,否则值转换为毫秒时必须在1和{@link整数#MAX_VALUE}之间。
*/
public Builder connectTimeout(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.");
if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");
connectTimeout = (int) millis;
return this;
}
这里我们设置时长的方法是有约束的
1.超时时长必须大于0
2.TimeUnit传值不能为空
3.超时时长不能大于整形最大值
4.超时时长不能在0到1之间
其他时长就不介绍了哈都是一样的处理
下面是设置代理
public Builder proxy(Proxy proxy) {
this.proxy = proxy;
return this;
}
这里的代理有3种模式 DIRECT(没有代理直接访问),HTTP (这样的代理是我们通常用的,它经常被协议处理程序(如HTTP,HTTPS和FTP)使用。),SOCKS(SOCKS类型代理 全能型代理支持多种协议)
设置代理选择器
public Builder proxySelector(ProxySelector proxySelector) {
this.proxySelector = proxySelector;
return this;
}
如果我们没有设置代理类型则proxySelector方法才会起作用如果未明确指定 proxy,请设置要使用的代理选择策略。 代理选择器可以返回多个代理; 在这种情况下,将按顺序尝试它们,直到建立成功的连接。
下面是设置cookie的方法
/**
* 设置可以接受来自传入HTTP响应的Cookie并向传出的HTTP请求提供Cookie的处理程序。
* <p>如果未设置,则将接受{@ linkplain CookieJar#NO_COOKIES无Cookie}。
*/
public Builder cookieJar(CookieJar cookieJar) {
if (cookieJar == null) throw new NullPointerException("cookieJar == null");
this.cookieJar = cookieJar;
return this;
}
setInternalCache 跟cache设置缓存相关内容之后的章节会讲到
public Builder dns(Dns dns) {
if (dns == null) throw new NullPointerException("dns == null");
this.dns = dns;
return this;
}
设置DNS
下面是设置socketFactory (还没用到过所以不太明白)
public Builder socketFactory(SocketFactory socketFactory) {
if (socketFactory == null) throw new NullPointerException("socketFactory == null");
this.socketFactory = socketFactory;
return this;
}
对于跟Https相关的设置可以查看跟https相关的内容这里不再过多介绍了
设置连接池
public Builder connectionPool(ConnectionPool connectionPool) {
if (connectionPool == null) throw new NullPointerException("connectionPool == null");
this.connectionPool = connectionPool;
return this;
}
后面的内容大家可以看注释