OkHttp-v4.9.1原理浅析

354 阅读4分钟

首先看一下OkHttp是怎样使用的,

  • 同步请求 通过execute方法执行
fun requestSync() {
    val request = Request.Builder()
        .url("https://www.baidu.com")
        .build()
    val client = OkHttpClient.Builder()
        .build()
    val call = client.newCall(request)
    val response = call.execute()
    println("同步请求")
    println(response.body?.string())
}
  • 异步请求 通过enqueue方法执行
fun requestASync() {
    val request = Request.Builder()
        .url("https://www.baidu.com")
        .build()
    val client = OkHttpClient.Builder()
        .build()
    val call = client.newCall(request)
    call.enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
        }

        override fun onResponse(call: Call, response: Response) {
            println("异步请求")
            println(response.body?.string())
        }
    })
}

可以看到不论是同步还是异步的方式,都是通过OkHttpClient创建一个Call,再通过Call执行网络请求,所以先从OkHttpClient开始分析。

OkHttpClient

首先看下OkHttpClient是怎样创建的,通过上面的代码很容易看出,OkHttpClient的创建使用的是建造者模式,设置一系列参数,最后通过OkHttpClient.Builder的build方法来构造返回OkHttpClient。通过源码可以验证

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
    @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
    //... 此处省略n个可配置的参数
    constructor() : this(Builder())
    
    init{
       // 初始化其它的一些参数
       // ...
       // 校验参数合法性
       verifyClientState()
    }
    
    class Builder constructor() {
        internal var dispatcher: Dispatcher = Dispatcher()
        //... 此处省略n个参数
        
        // 巧用kotlin的apply实现参数赋值
        fun dispatcher(dispatcher: Dispatcher) = apply {
          this.dispatcher = dispatcher
        }
        // ...
        // 构造返回OkHttpClient
        fun build(): OkHttpClient = OkHttpClient(this)
    }
}

至于OkHttpClient可以设置哪些参数,这里就不详细探究,本文只讲解OkHttpClient实现的大致流程。

RealCall

创建好OkHttpClient之后,还需要通过newCall方法创建一个RealCall对象

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
    // ...
    override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
    // ...
}

这里可以看到传入了一个Request对象,用于设置请求的地址,header等参数,Request类同样使用了建造者模式实现。

继续深入到RealCall类中去,通过开头的使用示例可以看到,同步请求使用的是RealCall的execute方法,异步请求使用的是enqueue方法,那么先从execute方法看起吧

class RealCall(
  val client: OkHttpClient,
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
    // 检查是否已经在执行网络请求,防止多线程情况下出现多次请求问题
    private val executed = AtomicBoolean()
    // ...
    override fun execute(): Response {
      check(executed.compareAndSet(false, true)) { "Already Executed" }
        
      // 计时开始,用于统计请求超时
      timeout.enter()
      // 通知用户请求开始,会回调一个用户主动设置的listener
      callStart()
      try {
        // 将请求添加到请求队列中
        client.dispatcher.executed(this)
        // 真正执行请求
        return getResponseWithInterceptorChain()
      } finally {
        // 将本次call从请求队列中移除
        client.dispatcher.finished(this)
      }
    }
}

再看异步请求是怎么做的

override fun enqueue(responseCallback: Callback) {
  // 检查是否已经在请求
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  callStart()
  // 封装一个AsyncCall,丢入到请求队列中
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

可以看到不论是同步还是异步,都使用到了一个叫Dispatcher的东西,将请求丢进了请求队列中去,所以接下来看看Dispatcher做了哪些工作。

Dispatcher

OkHttp提供了一个默认的Dispatcher,用于管理每一个请求,当然用户也可以自定义一个。先看看添加同步请求怎么实现的

class Dispatcher constructor() {
    // 同一时刻最大请求数
    @get:Synchronized var maxRequests = 64
    //...省略若干代码
    // 同一时刻URL或IP最大请求数
    @get:Synchronized var maxRequestsPerHost = 5
    //...省略若干代码
    // 异步等待队列
    private val readyAsyncCalls = ArrayDeque<AsyncCall>()
    // 异步执行队列
    private val runningAsyncCalls = ArrayDeque<AsyncCall>()
    // 同步执行队列
    private val runningSyncCalls = ArrayDeque<RealCall>()
    @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!!
      }
    @Synchronized internal fun executed(call: RealCall) {
      runningSyncCalls.add(call)
    }
}

很简单,就是将Call丢进了runningSyncCalls队列中。可以看到Dispatcher中维护了三个队列和一个线程池,用于管理Call。

接下来看异步

internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
    // 添加进异步等待队列
    readyAsyncCalls.add(call)

    // 不是socket请求
    if (!call.call.forWebSocket) {
       // 找到同一host的请求,用于统计限制同一host请求数
      val existingCall = findExistingCallWithHost(call.host)
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
    }
  }
  promoteAndExecute()
}

再看promoteAndExecute方法

private fun promoteAndExecute(): Boolean {
  this.assertThreadDoesntHoldLock()

  val executableCalls = mutableListOf<AsyncCall>()
  val isRunning: Boolean
  synchronized(this) {
    val i = readyAsyncCalls.iterator()
    while (i.hasNext()) {
      // 从等待队列中取一个Call
      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()
      // 将取到的Call丢进executableCalls中
      executableCalls.add(asyncCall)
      // 将从等待队列中取到的Call丢进runningAsyncCalls中
      runningAsyncCalls.add(asyncCall)
    }
    // 正在执行的同步异步请求数是>0
    isRunning = runningCallsCount() > 0
  }
    
  // 遍历执行Call
  for (i in 0 until executableCalls.size) {
    val asyncCall = executableCalls[i]
    // 调用AsyncCall中的executeOn方法
    asyncCall.executeOn(executorService)
  }

  return isRunning
}

Dispacher中还提供了一些请求结束,请求取消等等方法,这里就不再探究。

通过上面可以看到异步请求最终会封装成一个AsyncCall,并丢入到线程池中执行,那么AsyncCall一定是一个Runnable,所以接下来看下AsyncCall这个类

AsyncCall

AsyncCall是一个RealCall的内部类,这里主要看两个方法,executeOn和run方法

internal inner class AsyncCall(
  private val responseCallback: Callback
) : Runnable {
    fun executeOn(executorService: ExecutorService) {
      // 判断当前线程没有持有锁
      client.dispatcher.assertThreadDoesntHoldLock()

      try {
        // 通过线程池执行线程
        executorService.execute(this)
      } catch (e: RejectedExecutionException) {
        // 省略n行代码
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

    override fun run() {
      // 设置当前线程名称
      threadName("OkHttp ${redactedUrl()}") {
        // 计时开始
        timeout.enter()
        try {
          // 执行真正的请求
          val response = getResponseWithInterceptorChain()
          // 回调结果
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          // 请求失败
        } catch (t: Throwable) {
          cancel()
          // 请求失败
        } finally {
          // 请求结束
          client.dispatcher.finished(this)
        }
      }
    }
}

这里就可以看到和同步请求一样,都是调用了RealCall的getResponseWithInterceptorChain方法请求数据,只是返回数据的方式不一样而已。所以接下来再看看getResponseWithInterceptorChain方法。

getResponseWithInterceptorChain

很明显这里可以看到,OkHttp执行网络请求使用到了责任链模式,

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf<Interceptor>()
  // 用户自定义的拦截器,可以有多个
  interceptors += client.interceptors
  // 重试和重定向拦截器
  interceptors += RetryAndFollowUpInterceptor(client)
  // 桥接拦截器,主要是设置一些通用的header
  interceptors += BridgeInterceptor(client.cookieJar)
  // 缓存拦截器
  interceptors += CacheInterceptor(client.cache)
  // 连接拦截器
  interceptors += ConnectInterceptor
  // 如果不是socket,再添加网络拦截器,可以有多个
  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) {
    //...
  } finally {
    //...
  }
}

整个请求过程如图所示

未命名文件 (4).jpg

通过上面可以看到,当我们发起一个请求时,最先调用的是用户自定义的拦截器(应用拦截器),而网络拦截器,是到执行请求之前才执行的。

本次分析到此为止,至于各个拦截器的内部实现原理是怎样的,后面有空了再看看。