OkHttp 源码解析(4.9.1 版本)

文章目录

      • 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 {
    // OkHttp
  	implementation '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()
    // 注意子线程中不能操作更新 UI
    runOnUiThread {
        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()
        // 注意子线程中不能操作更新 UI
        runOnUiThread {
            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 常见对象介绍

OkHttp 源码解析(4.9.1 版本)_第1张图片

  • 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 重定向到 HTTPS
    internal 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 = 0
    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
    internal 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// 请求的 url
        internal 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(): Timeout

    public 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 保证在多线程下及时可见性与原子性
        @Volatile
        var callsPerHost = AtomicInteger(0)
            private set

        fun reuseCallsPerHostFrom(other: AsyncCall) {
            this.callsPerHost = other.callsPerHost
        }

        val host: String
            get() = originalRequest.url.host

        val request: Request
            get() = originalRequest

        val call: RealCall
            get() = this@RealCall

        fun executeOn(executorService: ExecutorService) {
            client.dispatcher.assertThreadDoesntHoldLock()

            var success = false
            try {
                // 调用线程池执行
                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 = false
                timeout.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:Synchronized
    var maxRequests = 64
        set(maxRequests) {
            require(maxRequests >= 1) { "max < 1: $maxRequests" }
            synchronized(this) {
                field = maxRequests
            }
            promoteAndExecute()
        }

    // 每个主机并发执行的最大请求数
    @get:Synchronized
    var maxRequestsPerHost = 5
        set(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:指定创建线程的工厂;
     * 

* 该线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间 * 为 60s(可以理解为 okhttp 随时可以创建新的线程来满足需要,可以保证网络的I/O任务有线程来处理 *,不被阻塞),使用的等待队列即 SynchronousQueue。 *

* SynchornousQueue:内部没有任何缓存的阻塞队列。这里表示接到新任务,直接交给线程处理,如果其 * 他的线程都在工作,那就创建一个新的线程来处理这个任务。 */
@get:Synchronized @get:JvmName("executorService") val executorService: ExecutorService get() { 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? = null
        internal var protocol: Protocol? = null
        internal var code = -1
        internal var message: String? = null
        internal var handshake: Handshake? = null
        internal var headers: Headers.Builder
        internal var body: ResponseBody? = null
        internal var networkResponse: Response? = null
        internal var cacheResponse: Response? = null
        internal var priorResponse: Response? = null
        internal var sentRequestAtMillis: Long = 0
        internal var receivedResponseAtMillis: Long = 0
        internal var exchange: Exchange? = null
    ... 
}

4、OkHttp 源码解析

OkHttp 源码解析(4.9.1 版本)_第2张图片

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、七层拦截器源码解析

OkHttp 源码解析(4.9.1 版本)_第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: ExecutorService
    get() {
        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: ExecutorService
    get() {
        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,这相对比于链表来说,数组的查找效率要更高。

你可能感兴趣的:(Android,第三方框架源码,OkHttp)