OkHttp源码分析(一)

OkHttp的简单使用

众所周知,讲解一个框架的原理之前都需要知道这个框架如何使用的,都不知道怎么使用的话,直接去分析原理的话,就显得有点头重脚轻了。OkHttp很庞大,当然一篇文章是说不清楚的。哈哈哈哈哈,废话不多说啊,我们先看看简单的使用吧。

val client = OkHttpClient.Builder()
            .connectTimeout(1000, TimeUnit.MILLISECONDS)
            .callTimeout(2000, TimeUnit.MILLISECONDS)
            .build()
        val request = Request.Builder()
            .url("https://www.baidu.com/")
            .build()

        client.newCall(request).enqueue(object : Callback {

            override fun onFailure(call: Call, e: IOException) {
                TODO("Not yet implemented")
            }

            override fun onResponse(call: Call, response: Response) {
                TODO("Not yet implemented")
            }

        })

这就是最简单的使用了,实现了一个Get请求,实例化OkHttpClient和Request,再调用newCall传入request对象,再调用enqueue方法实现异步请求,当然我们也可以使用同步调用,那我们就需要开启一个线程去做网络请求。最后就可以在回调方法里拿到成功或是失败的对象。
咱们的分析就从上面提到的OKHttpClient、Request、newCall、enequeue开始说起。

OkHttpClient

OkHttp请求的源头,分析原理的开始。那我们跟随它去看看他处理了什么呢。小儿,上代码。
由于这个类的代码很多,就不都贴出来了。只上一小段啊。

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
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
    internal var cache: Cache? = null
    internal var dns: Dns = Dns.SYSTEM
    internal var proxy: Proxy? = null
    internal var proxySelector: ProxySelector? = null
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()
    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

其实看这段这就可以了,这是OkHttpClient中的Builder类中的变量。有没有发现其中出现了很熟悉的属性,如connectTimeout、readTimeout等。
具体的源代码大家可以自己去看看,真的一大堆变量。那其实到这里大家应该会明白,在OkHttpClient中其实是做了变量的初始化,初始化OkHttp所必须的一大堆变量,例如分发器、拦截器、缓存、重定向、连接池等。,随便提一嘴,这里用到了建造者模式。建造者模式是什么呢?建造者模式的目的是为了分离对象的属性与创建过程。例如你想构建一个OkHttpClient,如果不用建造者,你就需要去了解这类里面的各个变量都是啥,然后决定要不要赋值,但是使用了建造者之后,你就可以指定几个变量就就可以,内部自己去完成复杂的对象构造过程。

Requset和Response

我们先看看Request里面都有啥吧。

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>
) {//....
}

请求的网络所封装的必须信息。请求的url,请求的方法、头信息、请求体。

再来看看Response里面都有啥。

class Response internal constructor(
 
  @get:JvmName("request") val request: Request,

  @get:JvmName("protocol") val protocol: Protocol,

  @get:JvmName("message") val message: String,

  @get:JvmName("code") val code: Int,

  @get:JvmName("handshake") val handshake: Handshake?,

  @get:JvmName("headers") val headers: Headers,

  @get:JvmName("body") val body: ResponseBody?,

  @get:JvmName("networkResponse") val networkResponse: Response?,

  @get:JvmName("cacheResponse") val cacheResponse: Response?,

  @get:JvmName("priorResponse") val priorResponse: Response?,

  @get:JvmName("sentRequestAtMillis") val sentRequestAtMillis: Long,

  @get:JvmName("receivedResponseAtMillis") val receivedResponseAtMillis: Long,

  @get:JvmName("exchange") internal val exchange: Exchange?
) : Closeable {
//.....
}

也是一些相关的变量。

newCall

重头戏来了,我们构建的Request的对象就是通过这个方法发出去的,承载着全村的希望,终于把这个孩子送出去了。那我们去看看内部的实现吧。看它怎么送出去的。

 /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

哦豁,newCall把Request交给了RealCall,它怎么敢的呀。把Request交出去了,返回给我们一个Call对象。然后Call对象去调用同步或异步方法。

RealCall这个类肯定是继承了Call,我们看看Call是个什么吧。

interface Call : Cloneable {
  /** Returns the original request that initiated this call. */
  fun request(): Request
  
  @Throws(IOException::class)
  fun execute(): Response

  fun enqueue(responseCallback: Callback)

  fun cancel()

  fun isExecuted(): Boolean

  fun isCanceled(): Boolean
  
  fun timeout(): Timeout

  public override fun clone(): Call

  fun interface Factory {
    fun newCall(request: Request): Call
  }
}

原来是一个接口啊,那么RealCall实现了这个接口,所以真实的请求还是交给RealCall来完成的。

enqueue

我们再来看看enqueue方法里面做了啥吧。

override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

其他的我们不看,我们只看主要的流程,我们看最后一行代码client.dispatcher.enqueue(),不用我说大家应该猜出来了client是OkHttpClient,最后是调用了client里面的dispatch也就是分发器去做做操作的。

有人就说道理我都懂,那这个AsyncCall是是什么东西呢,好像没见过啊。那我们去看看吧。

 internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
    //.....
  }

先看这个AsyncCall继承了Runable接口,那我们可以想到核心代码的执行逻辑肯定在run方法里面,这个大家应该知道吧,好吧,你应该知道了。让我们把run方法里面的代码单独拿出来。

override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        timeout.enter()
        try {
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
          } else {
            responseCallback.onFailure(this@RealCall, e)
          }
        } catch (t: Throwable) {
          cancel()
          if (!signalledCallback) {
            val canceledException = IOException("canceled due to $t")
            canceledException.addSuppressed(t)
            responseCallback.onFailure(this@RealCall, canceledException)
          }
          throw t
        } finally {
          client.dispatcher.finished(this)
        }
      }
    }

大家应该发现了其中里面的一个关键方法getResponseWithInterceptorChain(),看来这是去进行网络请求的关键方法啊。这个方法的返回值就是我们最后在onResponse拿到的值。

getResponseWithInterceptorChain

那我们就去这个方法里面看看具体的实现吧。

internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf<Interceptor>()
  interceptors += client.interceptors
  interceptors += RetryAndFollowUpInterceptor(client)
  interceptors += BridgeInterceptor(client.cookieJar)
  interceptors += CacheInterceptor(client.cache)
  interceptors += ConnectInterceptor
  if (!forWebSocket) {
    interceptors += client.networkInterceptors
  }
  interceptors += CallServerInterceptor(forWebSocket)

  val chain = RealInterceptorChain(
      call = this,
      interceptors = interceptors,
      index = 0,
      exchange = null,
      request = originalRequest,
      connectTimeoutMillis = client.connectTimeoutMillis,
      readTimeoutMillis = client.readTimeoutMillis,
      writeTimeoutMillis = client.writeTimeoutMillis
  )

  var calledNoMoreExchanges = false
  try {
    val response = chain.proceed(originalRequest)
    if (isCanceled()) {
      response.closeQuietly()
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      noMoreExchanges(null)
    }
  }
}

这就看到了我们的拦截器,OkHttp核心的责任链模式。首先是实例化了一个拦截器的list,然后把我们自定义的拦截器加上了,什么你不知道?我们自定义的拦截器?你看这个client眼不眼熟,那不就是OkHttpClient嘛。然后又把重试和重定向拦截器加上了,再把桥接拦截器加上了,又把缓存拦截器加上了,再把连接拦截器加上了,最后把服务器拦截器加上了。至于每个拦截器做了啥,咱们之后再细细分析哈。这里就简单说几句。

  • RetryAndFollowUpInterceptor:重试和重定向
  • BridgeInterceptor:构建网络请求需要的信息,如一些头部信息Content-Type、Content-Length等。
  • CacheInterceptor:顾名思义,做缓存
  • ConnectInterceptor:与服务器做连接的
  • CallServerInterceptor:与服务器通信,得到返回的数据

到这里,大家应该明白了,OkHttp的具体调用流程吧。那么有人就问了,不是链式调用嘛。那是在哪里调用的呢。

proceed

不知道大家有没有注意到proceed方法呢。就是在这里开启了链式调用。

override fun proceed(request: Request): Response {
  check(index < interceptors.size)

  calls++

  if (exchange != null) {
    check(exchange.finder.sameHostAndPort(request.url)) {
      "network interceptor ${interceptors[index - 1]} must retain the same host and port"
    }
    check(calls == 1) {
      "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
    }
  }

  // Call the next interceptor in the chain.
  val next = copy(index = index + 1, request = request)
  val interceptor = interceptors[index]

  @Suppress("USELESS_ELVIS")
  val response = interceptor.intercept(next) ?: throw NullPointerException(
      "interceptor $interceptor returned null")

  if (exchange != null) {
    check(index + 1 >= interceptors.size || next.calls == 1) {
      "network interceptor $interceptor must call proceed() exactly once"
    }
  }

  check(response.body != null) { "interceptor $interceptor returned a response with no body" }

  return response
}

大家看这段核心代码。

 // Call the next interceptor in the chain.
  val next = copy(index = index + 1, request = request)
  val interceptor = interceptors[index]

  @Suppress("USELESS_ELVIS")
  val response = interceptor.intercept(next) ?: throw NullPointerException(
      "interceptor $interceptor returned null")

先把index加一表示需要调用下一个拦截器,接着去调用当前拦截器的intercept方法并且把next传入进去。那我们随便看一个拦截器中的实现过程。

override fun intercept(chain: Interceptor.Chain): Response {
 //.......

  val networkResponse = chain.proceed(requestBuilder.build())

 //.......

  return responseBuilder.build()
}

上述代码就是BridgeInterceptor的intercept方法,我们看看里面也是调用了proceed方法,如此完成一个链式调用。

OkHttp请求流程

在这里插入图片描述
那么到这里主线流程是走完了。至于里面的细节我们下次再谈。敬请期待OkHttp源码解析(二)。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值