目录
0.前言
1.各个属性浅析
01.dispatcher
02.connectionPool
03.interceptors&networkInterceptors
04.eventListenerFactory
05.retryOnConnectionFailure
06.authenticator
07.followRedirects & followSslRedirects
08.cookieJar
09.cache
10.dns
11.proxy & proxySelector & proxyAuthenticator
12.socketFactory & sslSocketFactory
13.x509TrustManager
14.connectionSpecs
15.protocols
16.hostnameVerifier
17.certificatePinner
18.certificateChainCleaner
19.一组跟时间有关的属性 callTimeoutMillis & connectTimeoutMillis & readTimeoutMillis & writeTimeoutMillis & pingIntervalMillis
2.结语
0.前言
如期而至,相约与本周发布的文章。他来了哦~
首先,我想先讲一个点,就是这两篇文章都稍微比较长,但是如果真的能认真的看完,理解完(如果有读不懂的地方,我随时恭候大家来找我交流)。我相信,我们都能上到一个不错的 level 了。
还没有看上篇文章的,快去围观吧~【Android】OkHttp源码解读逐字稿(1)-拦截器
本篇文章,主要探索 OkHttpClient 的一些配置。
先上图,基于上次的代码,我们在OkHttpClient()后加上一个.我们就可以看到如下图的多个属性和方法。那具体他们的作用是什么呢?那就是我们本篇文章的任务了。走带着该死的好奇心,一起去瞧瞧。
进入到 OkHttpClient 的代码中,在刚开始的代码中,列出了若干个属性,就是我们要研究的。我先粘出来,这样方便我们一个个分析。(为了更好看到大局,我删除了大部分注释)
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
@get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool
@get:JvmName("interceptors") val interceptors: List<Interceptor> = builder.interceptors.toImmutableList()
@get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> = builder.networkInterceptors.toImmutableList()
@get:JvmName("eventListenerFactory") val eventListenerFactory: EventListener.Factory = builder.eventListenerFactory
@get:JvmName("retryOnConnectionFailure") val retryOnConnectionFailure: Boolean = builder.retryOnConnectionFailure
@get:JvmName("authenticator") val authenticator: Authenticator = builder.authenticator
@get:JvmName("followRedirects") val followRedirects: Boolean = builder.followRedirects
@get:JvmName("followSslRedirects") val followSslRedirects: Boolean = builder.followSslRedirects
@get:JvmName("cookieJar") val cookieJar: CookieJar = builder.cookieJar
@get:JvmName("cache") val cache: Cache? = builder.cache
@get:JvmName("dns") val dns: Dns = builder.dns
@get:JvmName("proxy") val proxy: Proxy? = builder.proxy
@get:JvmName("proxySelector") val proxySelector: ProxySelector =
when {
// Defer calls to ProxySelector.getDefault() because it can throw a SecurityException.
builder.proxy != null -> NullProxySelector
else -> builder.proxySelector ?: ProxySelector.getDefault() ?: NullProxySelector
}
@get:JvmName("proxyAuthenticator") val proxyAuthenticator: Authenticator =
builder.proxyAuthenticator
@get:JvmName("socketFactory") val socketFactory: SocketFactory = builder.socketFactory
private val sslSocketFactoryOrNull: SSLSocketFactory?
@get:JvmName("sslSocketFactory") val sslSocketFactory: SSLSocketFactory
get() = sslSocketFactoryOrNull ?: throw IllegalStateException("CLEARTEXT-only client")
@get:JvmName("x509TrustManager") val x509TrustManager: X509TrustManager?
@get:JvmName("connectionSpecs") val connectionSpecs: List<ConnectionSpec> =
builder.connectionSpecs
@get:JvmName("protocols") val protocols: List<Protocol> = builder.protocols
@get:JvmName("hostnameVerifier") val hostnameVerifier: HostnameVerifier = builder.hostnameVerifier
@get:JvmName("certificatePinner") val certificatePinner: CertificatePinner
@get:JvmName("certificateChainCleaner") val certificateChainCleaner: CertificateChainCleaner?
@get:JvmName("callTimeoutMillis") val callTimeoutMillis: Int = builder.callTimeout
@get:JvmName("connectTimeoutMillis") val connectTimeoutMillis: Int = builder.connectTimeout
@get:JvmName("readTimeoutMillis") val readTimeoutMillis: Int = builder.readTimeout
@get:JvmName("writeTimeoutMillis") val writeTimeoutMillis: Int = builder.writeTimeout
@get:JvmName("pingIntervalMillis") val pingIntervalMillis: Int = builder.pingInterval
1.各个属性浅析
01.dispatcher
如果仔细点的道友,可以发现在上篇文章中的开始分析的第一段代码中,就出现了它。当时,我们并没有分析它,那现在我们再来看看吧。
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
点击去,查看一下
runningSyncCalls.add(call)
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private val runningSyncCalls = ArrayDeque<RealCall>()
哦~看到上面的注释,我们知道了就是将当前的 call 放进了一个队列中 这还是一个同步的 call。
居然有同步,我们很自然就会想到异步这个概念。
正如我们所料,确实,还有另外两个队列。一个是 readyAsyncCalls, runningAsyncCalls。看注释,我们就知道他们的作用了。
/** Ready async calls in the order they'll be run. */
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
这时候,我们应该想起。我们使用 OkHttp 做一个网络请求时,我们还可以使用 enqueue 的方法
client.newCall(request).enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
同理 看到 RealCall 中对应的方法
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
发现,它调用了 dispatcher 的同名方法。
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
一样,看到最后一行
/**
* Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the
* executor service. Must not be called with synchronization because executing calls can call
* into user code.
*
* @return true if the dispatcher is currently running calls.
*/
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
看到注释,我们可以得知,这个方法,就是将一个可以被执行的 call 用 executor ervices 跑起来。看上面两个 if。只要经历过这两个 if 的筛选的话,就可以 executableCalls.add(asyncCall) 。
那我们这两个if ,主要做了什么。
1.判断正在执行的 call 的个数是否大于 maxRequests
2.判断同一个Host下的 call 个数是否大于 maxRequestsPerHost
至于,maxRequests 和 maxRequestsPerHost 就不细讲了。见明知意,maxRequests 默认值为 5 ,maxRequestsPerHost 默认值是 64。
我们看看 dispatchtor 这个类的注释,做一个小结
/**
* Policy on when async requests are executed.
*
* Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own
* executor, it should be able to run [the configured maximum][maxRequests] number of calls
* concurrently.
*/
大概意思,就是会通过一个 ExecutorService 去执行一个call。
最后,留一个疑问,“enqueue 方法,是通过 promoteAndExcute 方法,去调用一个 ExecutorService 去执行。那 execute() 是通过什么去运行一个 call的呢?”
02.connectionPool
这个跟我们的 ThreadPool ,具有异曲同工之妙
看下这个类的注释
* Manages reuse of HTTP and HTTP/2 connections for reduced network latency. HTTP requests that
* share the same [Address] may share a [Connection]. This class implements the policy
* of which connections to keep open for future use.
这里,单独说明了,HTTP 当同样的 IP 地址的时候,才可以共享一个连接。
那么 HTTP2 是具有多路复用(Multipex)的能力的。
03.interceptors & networkInterceptors
这个就是将我们自定义的拦截器接收下来,然后在我们上篇文章中的
getResponseWithInterceptorChain() 加入到 拦截器链条 中
04.eventListenerFactory
事件监听器工厂类,
EventListener
* Listener for metrics events. Extend this class to monitor the quantity, size, and duration of
* your application's HTTP calls.
05.retryOnConnectionFailure
这个返回的是个布尔值,所以这是个开关。
看名字,我们可以知道,“是否要在失败的时候,进行重试”
06.authenticator
自动认证修订的工具。
tokon 是一个有效期,这时候需要通过 refreshToken 去重新获取 token
07.followRedirects & followSslRedirects
同理,返回的是个布尔值,所以是个开关。
“再发生重定向的时候,是否要重定向”
“在发生协议重定向(http->https),是否要重定向”
08.cookieJar
CookieJar 类,主要有两个方法
fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>)
fun loadForRequest(url: HttpUrl): List<Cookie>
从相应头中保存 对应 url 的 cookies 信息
在请求头中带上 对应 url 的 cookies 信息
09.cache
* Caches HTTP and HTTPS responses to the filesystem so they may be reused, saving time and * bandwidth.
这个是缓存器,主要缓存一些数据,在下次获取的时候,可以直接从cache中获取,从而可以达到节省带宽的效果
10.dns
Domain name service 域名解析器。将 host 解析成对应的 IP 地址
核心代码
InetAddress.getAllByName(hostname).toList()
11.proxy & proxySelector & proxyAuthenticator
代理,如果需要请求需要通过代理服务器来负责,则添加一个代理。
这里需要注意一下它的类型,其中包括 直连,这也是默认方法。也就是不需要代理。
public enum Type {
/**
* Represents a direct connection, or the absence of a proxy.
*/
DIRECT,
/**
* Represents proxy for high level protocols such as HTTP or FTP.
*/
HTTP,
/**
* Represents a SOCKS (V4 or V5) proxy.
*/
SOCKS
};
而 proxy 的默认值为 null
internal var proxy: Proxy? = null
那怎么默认到 直连呢?那就是 proxySelector
when {
// Defer calls to ProxySelector.getDefault() because it can throw a SecurityException.
builder.proxy != null -> NullProxySelector
else -> builder.proxySelector ?: ProxySelector.getDefault() ?: NullProxySelector
}
如果 proxy 为空 且 默认的 proxySelector 也是空的话,就默认为 NullProxySelector 。而它最后返回的是 Type.DIRECT 的代理
public final static Proxy NO_PROXY = new Proxy();
// Creates the proxy that represents a {@code DIRECT} connection.
private Proxy() {
type = Type.DIRECT;
sa = null;
}
最后 proxyAuthenticator ,就是用于验证 代理服务器 的合法性的。
12.socketFactory & sslSocketFactory
我们进行 HTTP 连接请求本质上是一个 socket。 就是通过 socketFactory 去创建。
同时,我们进行加密连接 SSL 连接的时候,这是的 socket 就是通过 sslSocketFactory 去创建的。
13.x509TrustManager
首先 x509 是一种证书格式。这个类就是验证证书的合法性的。(如果不太明白,可以自行先了解一下 HTTPS 连接流程其中的安全性是如何保证的?后续,有空有把一些相关基础性的东西补充成另一篇文章)
14.connectionSpecs
连接标准。
在请求连接的时候,客户端需要向服务器,发送支持的协议,加密套件等信息 (看不懂,同上)
这里,我们还需要知道是,提供的四种配置。
/** A secure TLS connection that requires a recent client platform and a recent server. */
@JvmField
val RESTRICTED_TLS = Builder(true)
.cipherSuites(*RESTRICTED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.supportsTlsExtensions(true)
.build()
/**
* A modern TLS configuration that works on most client platforms and can connect to most servers.
* This is OkHttp's default configuration.
*/
@JvmField
val MODERN_TLS = Builder(true)
.cipherSuites(*APPROVED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.supportsTlsExtensions(true)
.build()
/**
* A backwards-compatible fallback configuration that works on obsolete client platforms and can
* connect to obsolete servers. When possible, prefer to upgrade your client platform or server
* rather than using this configuration.
*/
@JvmField
val COMPATIBLE_TLS = Builder(true)
.cipherSuites(*APPROVED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.supportsTlsExtensions(true)
.build()
/** Unencrypted, unauthenticated connections for `http:` URLs. */
@JvmField
val CLEARTEXT = Builder(false).build()
其中最后一种 明文传输就是 HTTP 。
第一种限制更多;第二种是比较流行的,同时也是默认值;第三种限制比较少,即兼容性更好。
15.protocols
支持的协议版本号,例如:HTTP_1_0; HTTP_1_1;HTTP_2;H2_PRIOR_KNOWLEDGE(不加密,明文传输的时候使用)
16.hostnameVerifier
在验证 证书的合法性的同时,我们还需要验证是这个证书是哪个网站的,那么就需要这个 hostnameVerifier 来验证。
17.certificatePinner
这个可以用来对某个网站,在验证证书的合法性同时,要满足我们指定的证书哈希值。但是不建议这么做,因为在更换验证机构后,会导致之前的用户无法正常使用我们的应用。
18.certificateChainCleaner
这是一个 X509TrustManager 的操作员
19.一组跟时间有关的属性 callTimeoutMillis & connectTimeoutMillis & readTimeoutMillis & writeTimeoutMillis & pingIntervalMillis
/**
* Default call timeout (in milliseconds). By default there is no timeout for complete calls, but
* there is for the connect, write, and read actions within a call.
*/
@get:JvmName("callTimeoutMillis") val callTimeoutMillis: Int = builder.callTimeout
/** Default connect timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("connectTimeoutMillis") val connectTimeoutMillis: Int = builder.connectTimeout
/** Default read timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("readTimeoutMillis") val readTimeoutMillis: Int = builder.readTimeout
/** Default write timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("writeTimeoutMillis") val writeTimeoutMillis: Int = builder.writeTimeout
/** Web socket and HTTP/2 ping interval (in milliseconds). By default pings are not sent. */
@get:JvmName("pingIntervalMillis") val pingIntervalMillis: Int = builder.pingInterval
其中需要额外了解下的是,最后一个 pingIntervalMillis。这是 HTTP2 的时候,可能是一个长连接,那么需要通过这个来进行保活,客户端发一个 ping,服务端回一个 pong
2.结语
这次的文章,主要是讲了这些属性的,以便我们在一些定制化需求上,知道我们可以用什么来做。然后具体的用法,其实自己尝试一下,或者搜一下就出来了。
如果文章中,有不懂的,或者纰漏,或者对我的文章写作排版有建议和意见的,都可以评论或者私信我。
|