Android okhttp4 kotlin版源碼淺析

本文基於源碼版本---4.7.2

實際上kotlin版相較於java版實現並無不同,調用流程基本一致,僅語法差異。

日常使用okhttp發送異步請求

    OkHttpClient().newCall(Request.Builder().build()).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {

        }

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

        }

    })

OkHttpClient().newCall(request)返回RealCall

class RealCall(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) 

RealCall.enqueue()

  override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

調用到Dispatcher.enqueue();傳參AsyncCall()

AsyncCall

  internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
    @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) {
     ...
    }

    override fun run() {
      ...
    }
  }

AsyncCall對RealCall進行包裝,實現Runnable接口重寫了run()方法

Dispatcher.enqueue(AsyncCall)

  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()
  }

先說說調度器Dispatcher,這裏只貼重點。

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()
    }

  //執行任務的線程池,等同於CacheThreadPool
  @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!!
    }

  //異步等待隊列
  private val readyAsyncCalls = ArrayDeque<AsyncCall>()

  //異步任務隊列
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()

  //同步任務隊列
  private val runningSyncCalls = ArrayDeque<RealCall>()
}

回到Dispatcher.enqueue()中,readyAsyncCalls.add(call)將請求加入等待隊列。

然後調用Dispatcher.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
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      //重點
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

這裏判斷當前請求數小於最大併發數,請求host小於最大請求host。

符合要求將任務從等待隊列移除,add進異步任務隊列。

然後調用AsyncCall.executeOn(executorService)

executorService即上文Dispatcher中初始化的線程池。

    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)
        responseCallback.onFailure(this@RealCall, ioException)
      } finally {
        if (!success) {
          client.dispatcher.finished(this)
        }
      }
    }

executorService.execute(this)

此處this即爲AsyncCall,前面說過AsyncCall實現了Runnable接口。

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)
        }
      }
    }
  }

重點val response = getResponseWithInterceptorChain()

很明顯通過攔截器鏈處理得到請求結果response,請求相關邏輯自然都在攔截器中。

RealCall.getResponseWithInterceptorChain()

  internal fun getResponseWithInterceptorChain(): Response {
    //各種攔截器
    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)
      }
    }
  }

val response = chain.proceed(originalRequest)

RealInterceptorChain.proceed()

 @Throws(IOException::class)
  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
  }

簡單說下攔截器的鏈式調用:

  • RealInterceptorChain保存了攔截器數組interceptors。
  • RealInterceptorChain.proceed()方法調用攔截器interceptor.intercept()方法處理request。
  • interceptor處理完request後繼續調用RealInterceptorChain.proceed()方法。
  • proceed()方法按照interceptors中攔截器順序依次調用,直到interceptors中所有攔截器處理完畢,返回結果response到上層攔截器。
  • intercept()方法返回值即爲response,逐級返回。

這也是爲什麼攔截器中可以同時處理request和response。

接下來看看okhttp默認添加的攔截器

    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)
  1. client.interceptors:用戶配置的攔截器。一般會統一添加請求頭,打印日誌等。
  2. RetryAndFollowUpInterceptor:失敗重試,重定向。
  3. BridgeInterceptor:配置請求頭。
  4. CacheInterceptor:用於緩存。
  5. ConnectInterceptor:連接服務器。
  6. networkInterceptors:用戶配置的network攔截器。
  7. CallServerInterceptor:發起網絡請求,獲取結果。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章