Okhttp源码分析

一、okhttp简单使用:

var client:OkHttpClient = OkHttpClient.Builder().build()
        val request:Request = Request.Builder().url("").build()
        //同步请求
        client.newCall(request).execute()
        //异步请求
        client.newCall(request).enqueue(object : Callback{
            override fun onFailure(call: Call, e: IOException) {

            }

            override fun onResponse(call: Call, response: Response) {

            }

        })
        
复制代码

二、异步使用源码查看

我们先看一下 client.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)
  
复制代码

可以看出它创建并且返回了一个RealCall对象

再看它的euqueue方法

override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    callStart()
    //diapatcher是线程池调度器
    client.dispatcher.enqueue(AsyncCall(responseCallback))
 }
复制代码

我们再看一下client.dispatcher.enqueue做了什么

 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()
  }
  
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
    }
    //把AsynCall对象添加到executableCalls中,遍历执行其executeOn方法
    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }
复制代码

上面代码的重点是这一句

  //把AsynCall对象添加到executableCalls中,遍历执行其executeOn方法
   for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)
   }
复制代码

我们来看一下 asyncCall.executeOn这个方法

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

  var success = false
  try {
    //executorService是一个线程池,它把AsyncCall(一个Runnable对象)添加进去了,
    //这样就可以在子线程中执行AsyncCall的run方法了
    executorService.execute(this)
    success = true
  } catch (e: RejectedExecutionException) {
    val ioException = InterruptedIOException("executor rejected")
    ioException.initCause(e)
    noMoreExchanges(ioException)
    responseCallback.onFailure(this@RealCall, ioException)
  } finally {
    if (!success) {
      client.dispatcher.finished(this) // This call is no longer running!
    }
  }
}
复制代码

如代码中:Okhttp网络请求异步操作是通过线程池executorService来实现的,它把AsyncCall(一个Runnable对象)添加进去了,这样就可以在子线程中执行AsyncCall的run方法了,我们来看一下AsyncCall的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)
        }
      }
    }
复制代码

在run方法中,执行了getResponseWithInterceptorChain(),并且返回了response,可见这个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)
      }
    }
  }
复制代码

它里面大致操作是,把所有的拦截器放到集合里面,创建一个RealInterceptorChain对象,在这个对象中调用proceed方法,在这个方法中,会调用到第一个拦截器的intercept方法,这个方法会传入包含了下一个拦截器对象的RealInterceptorChain对象,在intercept方法中,会执行拦截器相关的操作,然后又会调用RealInterceptorChain的proceed方法,依次循环调用,直到调用到CallServerInterceptor,它是最后一个拦截器,最终由它来执行网络请求。整个调用过程使用到了责任链设计模式。

在整个网络请求过程中,Okhttp会使用ConnectionPool连接池来缓存请求连接,以便在下次同一地址,同一端口的网络请求时,可以复用这个连接

在HTTP2.0版本之前,一个链接同时只能做一个请求,在HTTP2.0版本,一个链接可以同时做好几个网络请求,这就是多路复用技术

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享