文章目录

  • 1、OkHttp 简介
  • 2、OkHttp 配置与基本用法
    • 2.1 依赖引入与配置
    • 2.2 基本用法
  • 3、OkHttp 常见对象介绍
  • 4、OkHttp 源码解析
    • 4.1 当我们调用`okhttpClient.newCall(request).execute()`方法进行同步请求时:
    • 4.2 当我们调用`okhttpClient.newCall(request).equeue()`方法进行异步步请求时:
    • 4.3、七层拦截器源码解析
      • 4.3.1 全局拦截器
      • 4.3.2 重定向拦截器
      • 4.3.3 桥接拦截器
      • 4.3.4 缓存拦截器
      • 4.3.5 连接拦截器(ConnectInterceptor)层的连接复用池(ConnectionPool)
      • 4.3.6 非 WebSoket 拦截器
      • 4.3.7 请求服务拦截器
    • 4.4 异步请求线程池(位于 Dispatcher)
    • 4.5 连接池清理线程池(位于 ConnectionPool)
    • 4.6 缓存整理线程池(位于 DiskLruCache)
    • 4.7 HTTP2 异步事务线程池(位于 Http2Connection)
  • 5、OkHttp 总结
  • 6、OkHttp 相关问题?
    • 6.1 OkHttp 两种自定义拦截器什么区别?
    • 6.2 OkHttp 对于网络请求都有哪些优化,如何实现的?
    • 6.3 OkHttp 框架中都用到了哪些设计模式?
    • 6.4 为什么 OkHttp 好用呢?
    • 6.5 OkHttp 的底层是什么?
    • 6.6 OkHttp 中异步请求使用的线程池是什么样的?
    • 6.7 HTTP 1.0、HTTP 1.1、HTTP 2.0 之间区别是什么?
    • 6.8 Dispatcher 类中的三个请求队列为什么要使用 ArrayQueue 呢?

1、OkHttp 简介

  • HTTP 是现代应用常用的一种交换数据和媒体的网络方式,高效地使用 HTTP 能让资源加载更快,节省带宽。OkHttp 是一个高效的 HTTP 客户端,它有以下默认特性:
  • (1)支持 HTTP/2,允许所有同一个主机地址的请求共享同一个 socket 连接;
  • (2)连接池减少请求延时;
  • (3)透明的 GZIP 压缩减少响应数据的大小;
  • (4)缓存响应内容,避免一些完全重复的请求。
  • 当网络出现问题的时候 OkHttp 依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个 IP 地址,当第一个 IP 请求失败时,OkHttp 会交替尝试你配置的其他 IP,OkHttp 使用现代 TLS 技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到 TLS 1.0。
  • OkHttp 适用于 Android 5.0+(API 级别 21+)和 Java 8+。
  • OkHttp 依赖于 Okio 的高性能 I/O 和 Kotlin 标准库,两者都是具有强大向后兼容性的开源库。
  • 官方文档
  • Github

2、OkHttp 配置与基本用法

2.1 依赖引入与配置

// 模块的 build.gradle
dependencies {// OkHttpimplementation 'com.squareup.okhttp3:okhttp:4.9.1'// OkHttp 日志拦截器implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
}

2.2 基本用法

  • 初始化
// 设置拦截器
val httpLoggingInterceptor = HttpLoggingInterceptor() {Log.e("okhttp.OkHttpClient", it)
}
// 设置日志拦截器级别,默认为不打印输出日志
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
// 1.创建 OkHttpClient 对象,其内部也是 new Builder() 然后传到有一个参数的构造方法中
// ,也属于 Builder 模式
okHttpClient = OkHttpClient().newBuilder()// 设置连接超时为 10 秒.connectTimeout(10L, TimeUnit.SECONDS)// 设置文件读取超时为 60 秒.readTimeout(60L, TimeUnit.SECONDS)// 设置用于读取和写入缓存响应的响应缓存为 10M.cache(Cache(cacheDir, 10240 * 1024))// 设置 http 日志拦截器// 使用 addInterceptor() 也可以,即为第一层自定义拦截器// 使用 addNetworkInterceptor() 也可,即为第六层非网页网络拦截拦截器.addInterceptor(httpLoggingInterceptor).build()
// 2.创建 Request 对象,设置一个 url 地址和设置请求方式。
val request = Request.Builder().url("https://wanandroid.com/wxarticle/chapters/json").get() // 默认 GET 请求,可以不写.build()
// 3.创建一个 Call 对象,参数就是 Request 请求对象
val call = okHttpClient.newCall(request)
  • 同步请求
// 4.同步调用会阻塞主线程,这边在子线程进行,较少用
Thread(Runnable {try {// 同步调用,返回 Response,会抛出 IO 异常val response = call.execute()val data = response.body?.string()// 注意子线程中不能操作更新 UIrunOnUiThread {mViewBinding.tvOkhttpText.text = "onResponse: $data"}
} catch (e: IOException) {e.printStackTrace()runOnUiThread {mViewBinding.tvOkhttpText.text = "onFailure: ${e.message}"}
}
}).start()
  • 异步请求
// 4.异步请求加入调度,重写回调方法
call.enqueue(object : Callback {/*** 请求成功的回调方法** @param call Call* @param response Response*/override fun onResponse(call: Call, response: Response) {val data = response.body?.string()// 注意子线程中不能操作更新 UIrunOnUiThread {mViewBinding.tvOkhttpText.text = "onResponse: $data"}}/*** 请求失败的回调方法** @param call Call* @param e IOException*/override fun onFailure(call: Call, e: IOException) {e.printStackTrace()runOnUiThread {mViewBinding.tvOkhttpText.text = "onFailure: ${e.message}"}}
})

3、OkHttp 常见对象介绍

  • OkHttpClient:整个 OkHttp 的核心管理类,所有的内部逻辑和对象归 OkHttpClient 统一来管理,它通过 Builder 构造器生成,构造参数和类成员很多。
open class OkHttpClient internal constructor(  builder: Builder) : Cloneable, Call.Factory, WebSocket.Factory {...class Builder constructor() {internal var dispatcher: Dispatcher = Dispatcher() // 任务调度器internal var connectionPool: ConnectionPool = ConnectionPool()// 连接池internal val interceptors: MutableList<Interceptor> = mutableListOf()// 整体流程拦截器internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()// 网络流程拦截器internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()// 流程监听器internal var retryOnConnectionFailure = true// 连接失败时是否重连internal var authenticator: Authenticator = Authenticator.NONE// 服务器认证设置internal var followRedirects = true// 是否重定向internal var followSslRedirects = true// 是否从 HTTP 重定向到 HTTPSinternal var cookieJar: CookieJar = CookieJar.NO_COOKIES// cookie 设置internal var cache: Cache? = null// 缓存设置internal var dns: Dns = Dns.SYSTEM// DNS 设置internal var proxy: Proxy? = null// 代理设置internal var proxySelector: ProxySelector? = null// 代理选择器设置internal var proxyAuthenticator: Authenticator = Authenticator.NONE// 代理服务器认证设置internal var socketFactory: SocketFactory = SocketFactory.getDefault()// socket 配置internal var sslSocketFactoryOrNull: SSLSocketFactory? = null// 域名校验internal var x509TrustManagerOrNull: X509TrustManager? = null// 域名校验internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS// 域名校验internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS// 协议internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier// 域名校验internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT// 域名校验internal var certificateChainCleaner: CertificateChainCleaner? = null// 域名校验internal var callTimeout = 0// 默认请求超时时长internal var connectTimeout = 10_000// 默认连接超时时长internal var readTimeout = 10_000// 默认读取超时时长internal var writeTimeout = 10_000// 默认写入超时时长internal var pingInterval = 0internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZEinternal var routeDatabase: RouteDatabase? = null...
}
  • Request:请求参数的配置类,统一采用了 Builder 模式来构造参数(请求 url、请求方法、请求头、请求体)。
class Request internal constructor(@get:JvmName("url") val url: HttpUrl,@get:JvmName("method") val method: String,@get:JvmName("headers") val headers: Headers,@get:JvmName("body") val body: RequestBody?,internal val tags: Map<Class<*>, Any>
) {...open class Builder {internal var url: HttpUrl? = null// 请求的 urlinternal var method: String// 请求方法,如:GET、POST..internal var headers: Headers.Builder// 请求头internal var body: RequestBody? = null// 请求体...
}
  • Call:请求调用接口,其内部提供了同步请求、异步请求、取消请求等方法,其具体的实现类为 RealCall
/*** 调用是已准备好执行的请求,可以取消请求(已经完成的请求不能被取消)。* 由于该对象表示单个请求响应对(流),因此不能执行两次。*/
interface Call : Cloneable {/** 返回发起此调用的原始请求 */fun request(): Request/*** 同步请求,立即执行* 抛出两种异常:* (1)请求失败抛出 IOException;* (2)如果在执行过一回的前提下再次执行抛出 IllegalStateException。*/@Throws(IOException::class)fun execute(): Response/*** 异步请求,会将请求任务加入到对应的队列中再等待执行* 如果在执行过一回的前提下再次执行抛出 IllegalStateException。*/fun enqueue(responseCallback: Callback)/** 取消请求,已经完成的请求不能被取消 */fun cancel()/** 是否已被执行  */fun isExecuted(): Boolean/** 是否被取消   */fun isCanceled(): Boolean/** 请求超时时间配置 */fun timeout(): Timeoutpublic override fun clone(): Call/** 利用工厂模式来让 OkHttpClient 来创建 Call 对象 */fun interface Factory {fun newCall(request: Request): Call}
}
  • RealCallCall接口的具体实现类,可以通过OkHttpClient#newCall(request)创建一个Call对象。注意:RealCall有一个内部类AsyncCall,异步请求就是调用它,它就是一个Runnable,被调度器中的线程池执行。
// OkHttpClient.kt
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
class RealCall(val client: OkHttpClient, val originalRequest: Request,val forWebSocket: Boolean
) : Call {...internal inner class AsyncCall(private val responseCallback: Callback// 用户传入的响应回调方法) : Runnable {// 同一个域名的请求次数,volatile + AtomicInteger 保证在多线程下及时可见性与原子性@Volatilevar callsPerHost = AtomicInteger(0)private setfun reuseCallsPerHostFrom(other: AsyncCall) {this.callsPerHost = other.callsPerHost}val host: Stringget() = originalRequest.url.hostval request: Requestget() = originalRequestval call: RealCallget() = this@RealCallfun executeOn(executorService: ExecutorService) {client.dispatcher.assertThreadDoesntHoldLock()var success = falsetry {// 调用线程池执行executorService.execute(this)success = true} catch (e: RejectedExecutionException) {val ioException = InterruptedIOException("executor rejected")ioException.initCause(e)noMoreExchanges(ioException)// 请求失败,调用 Callback.onFailure() 方法responseCallback.onFailure(this@RealCall, ioException)} finally {if (!success) {// 请求失败,调用调度器 finish() 方法client.dispatcher.finished(this) // This call is no longer running!}}}override fun run() {threadName("OkHttp ${redactedUrl()}") {var signalledCallback = falsetimeout.enter()try {// 请求成功,获取到服务器返回的 response 数据val response = getResponseWithInterceptorChain()signalledCallback = true// 调用 Callback.onResponse() 方法,将 response 数据传递出去responseCallback.onResponse(this@RealCall, response)} catch (e: IOException) {if (signalledCallback) {// 不要两次发出回调信号Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)} else {// 请求失败,调用 Callback.onFailure() 方法responseCallback.onFailure(this@RealCall, e)}} catch (t: Throwable) {cancel()if (!signalledCallback) {val canceledException = IOException("canceled due to $t")canceledException.addSuppressed(t)// 请求失败,调用 Callback.onFailure() 方法responseCallback.onFailure(this@RealCall, canceledException)}throw t} finally {// 请求结束,调用调度器 finish() 方法client.dispatcher.finished(this)}}}}...
}
  • Dispatcher:调度器,用来调度Call对象,同时包含一个线程池和三个请求队列(1同2异),用来存放或者执行RealCall/AsyncCall对象。
class Dispatcher constructor() {// 并发执行的最大请求数@get:Synchronizedvar maxRequests = 64set(maxRequests) {require(maxRequests >= 1) { "max < 1: $maxRequests" }synchronized(this) {field = maxRequests}promoteAndExecute()}// 每个主机并发执行的最大请求数@get:Synchronizedvar maxRequestsPerHost = 5set(maxRequestsPerHost) {require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }synchronized(this) {field = maxRequestsPerHost}promoteAndExecute()}/** 正在运行的同步请求队列 */private val runningSyncCalls = ArrayDeque<RealCall>()/** 正在运行的异步请求队列 */private val runningAsyncCalls = ArrayDeque<RealCall.AsyncCall>()/** 准备执行的异步请求队列 */private val readyAsyncCalls = ArrayDeque<RealCall.AsyncCall>()/** 线程池 */private var executorServiceOrNull: ExecutorService? = null/*** 参数1:线程池中核心线程的最大数量;参数2:线程池中允许的最大线程数;* 参数3:空闲线程的存活时间;参数4:空闲线程存活的时间单位,与 keepAliveTime 配合使用;* 参数5:阻塞队列;参数6:指定创建线程的工厂;* </p>* 该线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间* 为 60s(可以理解为 okhttp 随时可以创建新的线程来满足需要,可以保证网络的I/O任务有线程来处理*,不被阻塞),使用的等待队列即 SynchronousQueue。* </p>* SynchornousQueue:内部没有任何缓存的阻塞队列。这里表示接到新任务,直接交给线程处理,如果其* 他的线程都在工作,那就创建一个新的线程来处理这个任务。*/@get:Synchronized@get:JvmName("executorService")val executorService: ExecutorServiceget() {if (executorServiceOrNull == null) {executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))}return executorServiceOrNull!!}
}
  • Callback:异步请求的回调接口。
interface Callback {/** 请求失败的回调方法 */fun onFailure(call: Call, e: IOException)/** 请求成功的回调方法 */@Throws(IOException::class)fun onResponse(call: Call, response: Response)
}
  • Response:请求返回响应的数据包装类,包含code、message、headers、body等等。
class Response internal constructor(...) : Closeable {...open class Builder {internal var request: Request? = nullinternal var protocol: Protocol? = nullinternal var code = -1internal var message: String? = nullinternal var handshake: Handshake? = nullinternal var headers: Headers.Builderinternal var body: ResponseBody? = nullinternal var networkResponse: Response? = nullinternal var cacheResponse: Response? = nullinternal var priorResponse: Response? = nullinternal var sentRequestAtMillis: Long = 0internal var receivedResponseAtMillis: Long = 0internal var exchange: Exchange? = null...
}

4、OkHttp 源码解析

4.1 当我们调用okhttpClient.newCall(request).execute()方法进行同步请求时:

  • 创建 OKHttpClient 实例,配置属性
  • 构建 Request ,配置属性
  • 构建 Realcall ,调用 executed
  • 执行 Dispatcher.executed() 中的 runningSyncCalls 将 Realcall 添加到同步执行队列中
  • 通过 getResponseWithInterceptorChain() 内部的责任链调用对 Request 层层处理包装,最后在 CallServerInterceptor 拦截器中发起请求和获得 Response
  • 通过 Dispatcher.finished(),把 call 实例从队列中移除,并执行下一次任务。

4.2 当我们调用okhttpClient.newCall(request).equeue()方法进行异步步请求时:

  • 创建 OKHttpClient 实例,配置属性
  • 构建 Request ,配置属性
  • 构建 Realcall,调用 enqueue
  • 生成一个 AsyncCall(responseCallback) 实例(实现了Runnable)
  • AsyncCall 实例放入了 Dispatcher.enqueue() 中,并判断 maxRequests (最大请求数 64)maxRequestsPerHost(最大host请求数 5)是否满足条件,如果满足就把 AsyncCall 添加到 runningAsyncCalls 中,并放入线程池中执行;如果条件不满足,就添加到等待就绪的异步队列,当那些满足的条件的执行时,在 Dispatcher.finifshed(this) 中的 promoteCalls() ;方法中 对等待就绪的异步队列进行遍历,生成对应的 AsyncCall 实例,并添加到 runningAsyncCalls 中,最后放入到线程池中执行,一直到所有请求都结束。

4.3、七层拦截器源码解析


)

4.3.1 全局拦截器
4.3.2 重定向拦截器
4.3.3 桥接拦截器
4.3.4 缓存拦截器
4.3.5 连接拦截器(ConnectInterceptor)层的连接复用池(ConnectionPool)
  • ConnectionPool在内部维护了一个线程池,用来执行清理连接任务cleanupRunnable,还维护了一个双端队列connections,用来缓存已经创建的连接。要知道创建一次连接要经历TCP握手,如果是HTTPS还要经历TLS握手,握手的过程都是耗时的,所以为了提高效率,就需要connections来对连接进行缓存,从而可以复用;还有如果连接使用完毕,长时间不释放,也会造成资源的浪费,所以就需要cleanupRunnable定时清理无用的连接,okhttp支持5个并发连接,默认每个连接keepAlive为5分钟,keepAlive就是连接空闲后,保持存活的时间。
  • 当我们第一次调用ConnectionPool的put方法缓存新建连接时,如果cleanupRunnable还没执行,它首先会使用线程池执行cleanupRunnable,然后把新建连接放入双端队列,cleanupRunnable中会调用cleanup方法进行连接的清理,该方法返回现在到下次清理的时间间隔,然后调用wiat方法进入等待状态,等时间到了后,再次调用cleanup方法进行清理,就这样往复循环。
4.3.6 非 WebSoket 拦截器
4.3.7 请求服务拦截器

4.4 异步请求线程池(位于 Dispatcher)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k8OrcbUZ-1648586078114)(https://note.youdao.com/yws/res/26063/09FE1BC412DC44958231F99730803E98)]

/*** 参数1:线程池中核心线程的最大数量;参数2:线程池中允许的最大线程数;* 参数3:空闲线程的存活时间;参数4:空闲线程存活的时间单位,与 keepAliveTime 配合使用;* 参数5:阻塞队列;参数6:指定创建线程的工厂;*/
@get:Synchronized
@get:JvmName("executorService")
val executorService: ExecutorServiceget() {if (executorServiceOrNull == null) {executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))}return executorServiceOrNull!!}
  • 该线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间为 60s(可以理解为 OkHttp 随时可以创建新的线程来满足需要,可以保证网络的I/O任务有线程来处理,不被阻塞),使用的等待队列即 SynchronousQueue。
  • SynchornousQueue:内部没有任何缓存的阻塞队列。这里表示接到新任务,直接交给线程处理,如果其他的线程都在工作,那就创建一个新的线程来处理这个任务。

4.5 连接池清理线程池(位于 ConnectionPool)

4.6 缓存整理线程池(位于 DiskLruCache)

4.7 HTTP2 异步事务线程池(位于 Http2Connection)

  • TaskRunner 中的 Backend

5、OkHttp 总结

  • OkHttp 中有两个比较重要的核心类,一个是 Dispatcher 类,一个是 Interceptor 类,其中 Dispatcher 类中维护了三个双向队列和一个线程池,三个双向队列分别为正在运行的同步请求队列、正在运行的异步请求队列,准备执行的异步请求队列,并且 Dispatcher 类指定的最大并发数为 64,每个主机最大请求数为 5。
  • 而 Interceptor 就是我们所说的拦截器,也是 OKHttp 的精髓所在,OkHttp 提供了 5 个默认和 2 个用户可传入的拦截器,这 7 个拦截器分别全局拦截器(interceptor)、重定向拦截器(重试和失败,RetryAndFollowUpInterceptor)、桥接拦截器(处理头部信息和 gzip 等,BridgeInterceptor)、缓存拦截器(CacheInterceptor)、连接拦截器(ConnectInterceptor)、非 WebSoket 网络拦截器(networkInterceptor)、请求服务器拦截器(将 http 请求写进 IO 流当中,并且从 IO 流中读取响应 Response),这七个拦截器采用了责任链模式一个接一个来完成整体的请求流程。
  • 当我们去进行同步网络请求的时候,首先将这个请求添加到同步请求队列中,然后等待被执行,当被执行时会通过责任链模式对依次调用这些拦截器对Request去进行包装,然后在最后一个拦截器发起请求并且回调 Callback(成功或者失败),最终将其重正在运行的异步请求队列中移除并执行下一个请求。
  • 当我们去进行异步网络请求的时候,首先通过最大请求数和最大 host 请求数去进行判断,满足则加入到正在运行的异步请求队列中,并放入线程池中执行,不满足则加入到准备执行的异步请求队列中,当被执行时同样会通过责任链模式对依次调用这些拦截器对Request去进行包装,然后在最后一个拦截器发起请求并且回调 Callback(成功或者失败),最终将其重正在运行的异步请求队列中移除并执行下一个请求。
  • 连接拦截器(ConnectInterceptor),其内部维护了一个网络复用池(ConnectionPool),通过一个双端队列来缓存已建立的连接和一个 Runnable 来清理请求连接任务,要知道创建一次连接要经历 TCP 握手,如果是 HTTPS 还要经历 TLS 握手,握手的过程都是耗时的,所以为了提高效率,就需要 connections 来对连接进行缓存,从而可以复用;还有如果连接使用完毕,长时间不释放,也会造成资源的浪费,所以就需要 cleanupRunnable 定时清理无用的连接,OkHttp 支持 5 个并发连接,默认每个连接 keepAlive 为 5 分钟,keepAlive 就是连接空闲后,保持存活的时间。
  • OkHttp 的线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间为 60s,可以理解为 OkHttp 随时可以创建新的线程来满足需要,可以保证网络的 I/O 任务有线程来处理,不被阻塞。
  • OkHttp 的最底层是 Socket,而不是 URLConnection,它通过 Platform 的 Class.forName() 反射获得当前运行时使用的 socket 库。其跟服务器进行数据交互的 IO 操作是通过 okio 开源库来进行的。

6、OkHttp 相关问题?

6.1 OkHttp 两种自定义拦截器什么区别?

  • 第一类是全局的 interceptor(自定义拦截器),归类于第一层拦截器且可添加多个,通过 OkHttpClient.Builder#addInterceptor(Interceptor) 方法传入,最终用 List 集合进行存储。
  • 另一类是非 WebSocket 网络拦截器的 interceptor(自定义拦截器),归类于第六层拦截器且可添加多个,这类拦截器只会在非 WebSocket 请求中被调用,通过 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 方法传入,最终用 List 集合进行存储。

6.2 OkHttp 对于网络请求都有哪些优化,如何实现的?

  • 通过连接池来减少请求延时:我们知道,在 OkHttp 中,我们每次的 request 请求都可以理解为一个 connection,而每次发送请求的时候我们都要经过 tcp 的三次握手,然后传输数据,最后在释放连接。在高并发或者多个客户端请求的情况下,多次创建就会导致性能低下。如果能够 connection 复用的话,就能够很好地解决这个问题了。能够复用的关键就是客户端和服务端能够保持长连接,并让一个或者多个连接复用。怎么保持长连接呢?在 BridgeInterceptor的intercept() 方法中 requestBuilder.header(“Connection”,“Keep-Alive”),我们在 request 的请求头添加了 (“Connection”, “Keep-Alive”) 的键值对,这样就能够保持长连接。而连接池 ConnectionPoll 就是专门负责管理这些长连接的类。需要注意的是,我们在初始化 ConnectionPoll 的时候,其构造方法会设置闲置的 connections 的最大数量为 5 个,每个最长的存活时间为 5 分钟。
  • 无缝支持 GZIP 来减少数据流量。
  • 缓存响应数据来减少重复的网络请求。
  • 可以从很多常用的连接问题中自动恢复。

6.3 OkHttp 框架中都用到了哪些设计模式?

  • Builder 模式:OkHttpClient 比较复杂,太多属性,而且客户的组合需求多样化,所以 OKhttp 使用建造者模式,Rquest 和 Response 同理。
  • 责任链模式:在 OkHttp 中的拦截器模块,执行过程用到 OkHttp 的拦截器链中,内置了 5 个默认的拦截器,分别用于重定向、桥接、缓存、链接、网络读写。
  • 策略模式 :CacheInterceptor 实现了数据的选择策略,来自网络还是来自本地。

6.4 为什么 OkHttp 好用呢?

  • 支持 http2,对一台机器的所有请求共享同一个 socket;
  • 内置连接池,支持连接复用,减少延迟;
  • 支持透明的 gzip 压缩响应体;
  • 通过缓存避免重复的请求;
  • 请求失败时自动重试主机的其他 ip,自动重定向;
  • 好用的 API。

6.5 OkHttp 的底层是什么?

  • OkHttp 的底层是 Socket,而不是 URLConnection,它通过 Platform 的 Class.forName() 反射获得当前 Runtime 使用的 socket 库。
  • socket 发起网络请求的流程一般是:
  • (1)创建socket对象;
  • (2)连接到目标网络;
  • (3)进行输入输出流操作。
  • 其中 1 和 2 的实现,封装在 connection 接口中,具体的实现类是 RealConnection。3 是通过 stream 接口来实现,根据不同的网络协议,有 Http1xStream 和 Http2xStream 两个实现类。
  • 由于创建网络连接的时间较久(如果是 HTTP 的话,需要进行三次握手),而请求经常是频繁的碎片化的,所以为了提高网络连接的效率,OKHttp 实现了网络连接复用。
  • 这里是否能进行网络连接复用的判断是通过当前请求的 address 和 host 是否跟缓存中的 address 和 host 一致来判定的。

6.6 OkHttp 中异步请求使用的线程池是什么样的?

/*** 参数1:线程池中核心线程的最大数量;参数2:线程池中允许的最大线程数;* 参数3:空闲线程的存活时间;参数4:空闲线程存活的时间单位,与 keepAliveTime 配合使用;* 参数5:阻塞队列;参数6:指定创建线程的工厂;*/
@get:Synchronized
@get:JvmName("executorService")
val executorService: ExecutorServiceget() {if (executorServiceOrNull == null) {executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))}return executorServiceOrNull!!}
  • 该线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间为 60s(可以理解为 OkHttp 随时可以创建新的线程来满足需要,可以保证网络的I/O任务有线程来处理,不被阻塞),使用的等待队列即 SynchronousQueue。
  • SynchornousQueue:内部没有任何缓存的阻塞队列。这里表示接到新任务,直接交给线程处理,如果其他的线程都在工作,那就创建一个新的线程来处理这个任务。

6.7 HTTP 1.0、HTTP 1.1、HTTP 2.0 之间区别是什么?

  • HTTP 1.0 和 HTTP 1.1 区别?
  • http 1.1 支持长连接(keep-alive)。
  • http 1.1 支持 host 域。
  • http 1.1 引入了更多缓存控制策略(例如:Entity tag)。
  • http 1.1 新增 24 个错误状态响应码。
  • HTTP 1.1 和 HTTP 2.0 区别?
  • http 2.0 支持多路复用:HTTP 1.1 中的消息是“管道串形化”的,只有等一个消息完成之后,才能进行下一条消息;而 HTTP 2.0 可以多个消息交织在一起,就是在一个 TCP 连接中可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求,通过这个技术可极大的提高传输性能。
  • http 2.0 支持头部数据压缩。
  • http 2.0 新增服务器推送,这是一种在客户端请求之前发送数据的机制。

6.8 Dispatcher 类中的三个请求队列为什么要使用 ArrayQueue 呢?

  • 他们都是用来存放网络请求的,这些请求需要做到先到先得,所以采用队列。
  • 当执行 enqueue() 方法进行异步请求时,我们需要遍历 readyAsyncCalls,将符合执行条件的 Call 加入到 runningAsyncCalls,这相对比于链表来说,数组的查找效率要更高。

OkHttp 源码解析(4.9.1 版本)相关推荐

  1. 彻底理解OkHttp - OkHttp 源码解析及OkHttp的设计思想

    OkHttp 现在统治了Android的网络请求领域,最常用的框架是:Retrofit+okhttp.OkHttp的实现原理和设计思想是必须要了解的,读懂和理解流行的框架也是程序员进阶的必经之路,代码 ...

  2. okhttp源码解析(五):代理和DNS

    前言 之前我们分析了okhttp的重试机制,发现在获取可用地址的时候,都需要遍历一个路由选择器,里面保存了可用的地址,那么这些地址是从哪来的呢?这就是本篇分析的重点. 首先我们简单理解一下代理和DNS ...

  3. OkHttp源码解析(上)

    导语 学过Android开发的同学都知道,我们使用网络请求获取服务器的数据时,通常使用的是封装好的Retrofit框架,这个框架很好的帮助我们对网络的发起,以及返回的数据进行操作,我们使用起来十分方便 ...

  4. okHttp源码解析------待续

    看该篇文章前首先要熟悉okHttp的使用,建议先读OkHttp的简单使用 本文的源码解析参考链接:okhttp3总和解析 1.从URL请求处理开始分析 由异步将请求加入调度方法开始引入正题: getC ...

  5. OkHttp源码解析(很细 很长)

    前言 本文是对OkHttp开源库的一个详细解析,如果你觉得自己不够了解OkHttp,想进一步学习一下,相信本文对你会有所帮助. 本文包含了详细的请求流程分析.各大拦截器解读以及自己的一点反思总结,文章 ...

  6. okhttp源码解析

    OkHttp是一个非常优秀的网络请求框架,已被谷歌加入到Android的源码中.目前比较流行的Retrofit也是默认使用OkHttp的.所以OkHttp的源码是一个不容错过的学习资源,学习源码之前, ...

  7. Android 网络编程之OkHttp源码解析

    前言:OkHttp框架是Android的网络请求框架,无数的项目都在使用着这个框架,重要性不言而喻; 本文会将OKHTTP的源码进行拆解,每个部分来单独学习,由简入深,循序渐进,篇幅较长,建议收藏,慢 ...

  8. Android OkHttp 源码解析 - 拦截器

    一.前言 看一下 RealCall 中的拦截器排序: Response getResponseWithInterceptorChain() throws IOException {// Build a ...

  9. android okhttp 源码解析,OkHttp 知识梳理(1) - OkHttp 源码解析之入门

    OkHttp 知识梳理系列文章 一.简介 OkHttp无疑是目前使用的最多的网络框架,现在最火的Retrofit也就是基于OkHttp来实现的,和以前一样,我们将从最简单的例子开始,一步步剖析OkHt ...

最新文章

  1. ASP.NET遍历配置文件的连接字符串
  2. 为电商而生的知识图谱,如何感应用户需求?
  3. Java RTTI与反射(参照Java编程思想与新浪博客)
  4. react-router 按需加载
  5. Chrome View Source Code 那些事
  6. python动态表情包下载_Python从eif中导出qq表情的gif图片
  7. 【转】微信开发出现“该公众号暂时无法提供服务,请稍后再试”的坑
  8. 【Python】第三方库安装脚本
  9. mysql plus baomidou_com.baomidou.mybatisplus.core.mapper 不存在
  10. 度身定造的女孩子C程序
  11. 有人公开了Avast、McAfee 等杀软中的 8 个 0day
  12. 技术支持在大数据分析中的作用
  13. 常见的Ajax写法汇总
  14. 2020 阿里、字节iOS面试题之Runtime相关问题2
  15. android安卓手机分屏多窗口实现方法
  16. grab显示连不上服务器,grab 暂时链接不到服务器
  17. 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(4)- Flashloader初体验(blhost)...
  18. 2019最新计算机毕业设计-题目汇总大全-系列4
  19. JavaWeb学习笔记:拦截器
  20. 用java写计算器(包括保存记录到文件和计算行列式功能)

热门文章

  1. CPU套路篇:cpu性能优化的几个思路???
  2. 录屏软件Kap使用经验分享
  3. Shiro与SpringBoot整合,实现登录拦截、用户认证、用户授权等。实战demo
  4. 在一个地方上班上久了,会出现想辞职的想法吗?
  5. 2019超值电话卡——校园卡最新消息,物联网卡推荐
  6. 全球征集 | “Kaleidoverse 万千灵境”元宇宙作品设计大赛
  7. epson服务器连接状态断开,爱普生打印机常见故障解决方法
  8. 003.update窗口缩放
  9. 人为什么会参与活动?
  10. 用HTML+CSS写一个手机微信界面