OkHttp 流程分析

1、創(chuàng)建Client
Builder 是OkHttpClient的一個內(nèi)部類停士,使用的是構(gòu)建者模式

val client = OkHttpClient.Builder().connectTimeout(5000, TimeUnit.MILLISECONDS).build()

// Builder
class Builder constructor() {
  internal var dispatcher: Dispatcher = Dispatcher() // 創(chuàng)建分發(fā)器對象
  ...
}

2挖帘、構(gòu)建Request
也就是構(gòu)建請求報文信息,請求的url,header等信息恋技,也是用的Builder模式

val request = Request.Builder().url(url).build()

3拇舀、創(chuàng)建Call 對象,可以把Call 對象當成是Request 和 Response 的中間橋梁蜻底,
Call 是一個接口骄崩,真正的實現(xiàn)類是RealCall舌涨。

val call = client.newCall(request)

// OkHttpClient.newCall
override fun newCall(request: Request): Call {
  return RealCall.newRealCall(this, request, forWebSocket = false)
}

// RealCall.newRealCall
fun newRealCall(
    client: OkHttpClient,
    originalRequest: Request,
    forWebSocket: Boolean
  ): RealCall {
    // Safely publish the Call instance to the EventListener.
    return RealCall(client, originalRequest, forWebSocket).apply {
      transmitter = Transmitter(client, this)
    }
  }
}

同步請求:

val response = call.execute()

// RealCall.execute
override fun execute(): Response {
    // 一個RealCall 只能執(zhí)行一次
  synchronized(this) {
    check(!executed) { "Already Executed" }
    executed = true
  }
  transmitter.timeoutEnter()
  transmitter.callStart()
  try {
    client.dispatcher.executed(this)
    // 通過攔截器鏈獲取Response
    return getResponseWithInterceptorChain()
  } finally {
   
    client.dispatcher.finished(this)
  }
}

// Dispatcher.executed
// 把call 添加到runningSyncCalls 隊列中
@Synchronized internal fun executed(call: RealCall) {
  runningSyncCalls.add(call)
}

// Dispatcher.finished
internal fun finished(call: AsyncCall) {
  call.callsPerHost().decrementAndGet()
  finished(runningAsyncCalls, call)
}

private fun <T> finished(calls: Deque<T>, call: T) {
  val idleCallback: Runnable?
  synchronized(this) {
      // 同步隊列中移除這個call
    if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
    idleCallback = this.idleCallback
  }
  
  // 查看是否還有請求诫龙,如有再繼續(xù)執(zhí)行
  val isRunning = promoteAndExecute()

  if (!isRunning && idleCallback != null) {
    idleCallback.run()
  }
}

異步請求:

val response2 = call.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")
    }
})


// ReaCall.enqueue
override fun enqueue(responseCallback: Callback) {
  synchronized(this) {
    check(!executed) { "Already Executed" }
    executed = true
  }
  transmitter.callStart()
  // 將new 一個AsyncCall驹饺,AsyncCall 是一個Runnable,是RealCall 的一個內(nèi)部類
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

// Dispatcher.enqueue
internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
    // 在等待隊列中添加AsyncCall
    readyAsyncCalls.add(call)

    if (!call.get().forWebSocket) {
      val existingCall = findExistingCallWithHost(call.host())
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
    }
  }
  promoteAndExecute()
}

// Dispatcher.promoteAndExecute
private fun promoteAndExecute(): Boolean {
  assert(!Thread.holdsLock(this))

  val executableCalls = mutableListOf<AsyncCall>()
  val isRunning: Boolean
  synchronized(this) {
    val i = readyAsyncCalls.iterator()
    while (i.hasNext()) {
      val asyncCall = i.next()
      
      // 判斷異步運行隊列是否大于等于 64 且同主機請求個數(shù) 是否大于等于5
      if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
      if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity.

      i.remove()
      asyncCall.callsPerHost().incrementAndGet()
      // 條件成立泌辫,則將等到請求隊列的請求 readyAsyncCalls 添加到 executableCalls 和 runningAsyncCalls 中
      executableCalls.add(asyncCall)
      runningAsyncCalls.add(asyncCall)
    }
    isRunning = runningCallsCount() > 0
  }
  
  // 將剛剛添加到runningAsyncCalls 中的請求蜓堕,放在線程池中執(zhí)行
  for (i in 0 until executableCalls.size) {
    val asyncCall = executableCalls[i]
    asyncCall.executeOn(executorService)
  }

  return isRunning
}

// Dispatcher.executorService
// 異步請求線程池
// 核心線程是為0,這樣沒有任務的時候苹享,就可以線程全部關(guān)掉阳谍, 最大線程數(shù)是無限大采记,其實不是的窿春,
// 因為異步運行隊列的最大個數(shù)限制在了64
// 超時時間的是60秒
@get:JvmName("executorService") val executorService: ExecutorService
  get() {
    if (executorServiceOrNull == null) {
      executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
          SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))
    }
    return executorServiceOrNull!!
  }
  
  // AsyncCall.enqueue
  fun executeOn(executorService: ExecutorService) {
  assert(!Thread.holdsLock(client.dispatcher))
  var success = false
  try {
    // 執(zhí)行AsyncCall 里面的run 方法
    executorService.execute(this)
    success = true
  } catch (e: RejectedExecutionException) {
    val ioException = InterruptedIOException("executor rejected")
    ioException.initCause(e)
    transmitter.noMoreExchanges(ioException)
    responseCallback.onFailure(this@RealCall, ioException)
  } finally {
    if (!success) {
      client.dispatcher.finished(this) // This call is no longer running!
    }
  }
}

// AsyncCall.run
override fun run() {
  threadName("OkHttp ${redactedUrl()}") {
    var signalledCallback = false
    transmitter.timeoutEnter()
    try {
      // 最后調(diào)用 RealCall的 getResponseWithInterceptorChain 獲取請求結(jié)果
      val response = getResponseWithInterceptorChain()
      signalledCallback = true
      responseCallback.onResponse(this@RealCall, response)
    } catch (e: IOException) {
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log(INFO, "Callback failure for ${toLoggableString()}", e)
      } else {
        responseCallback.onFailure(this@RealCall, e)
      }
    } finally {
      client.dispatcher.finished(this)
    }
  }
}

同步請求和異步請求都是從攔截器鏈中獲取結(jié)果拉一,采用的是責任鏈模式:

@Throws(IOException::class)
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(interceptors, transmitter, null, 0, originalRequest, this,
      client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)

  var calledNoMoreExchanges = false
  try {
    val response = chain.proceed(originalRequest)
    if (transmitter.isCanceled) {
      response.closeQuietly()
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw transmitter.noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null)
    }
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旧乞,隨后出現(xiàn)的幾起案子蔚润,更是在濱河造成了極大的恐慌,老刑警劉巖尺栖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫡纠,死亡現(xiàn)場離奇詭異,居然都是意外死亡延赌,警方通過查閱死者的電腦和手機除盏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來皮胡,“玉大人痴颊,你說我怎么就攤上這事÷藕兀” “怎么了蠢棱?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵锌杀,是天一觀的道長。 經(jīng)常有香客問我泻仙,道長糕再,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任玉转,我火速辦了婚禮突想,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘究抓。我一直安慰自己猾担,他們只是感情好,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布刺下。 她就那樣靜靜地躺著绑嘹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橘茉。 梳的紋絲不亂的頭發(fā)上工腋,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音畅卓,去河邊找鬼擅腰。 笑死,一個胖子當著我的面吹牛翁潘,可吹牛的內(nèi)容都是我干的趁冈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼唐础,長吁一口氣:“原來是場噩夢啊……” “哼箱歧!你這毒婦竟也來了矾飞?” 一聲冷哼從身側(cè)響起一膨,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洒沦,沒想到半個月后豹绪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡申眼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年瞒津,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片括尸。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡巷蚪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出濒翻,到底是詐尸還是另有隱情屁柏,我是刑警寧澤啦膜,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站淌喻,受9級特大地震影響僧家,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裸删,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一八拱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涯塔,春花似錦肌稻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至每聪,卻和暖如春旦棉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背药薯。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工绑洛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人童本。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓真屯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親穷娱。 傳聞我的和親對象是個殘疾皇子绑蔫,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容