OkHttp 4源碼(1)— OkHttp初始化和請(qǐng)求構(gòu)造分析

本文基于OkHttp 4.3.1源碼分析
OkHttp - 官方地址
OkHttp - GitHub代碼地址

概括

本篇主要從OkHttp的兩個(gè)請(qǐng)求示例開(kāi)始率挣,對(duì)Okhttp的初始化工作刻伊,和請(qǐng)求從構(gòu)造、分發(fā)到執(zhí)行的流程進(jìn)行源碼分析介紹

OkHttp整體流程(本文覆蓋紅色部分)

本文覆蓋代碼流程圖

示例

使用OkHttp一般流程椒功,初始化一個(gè)共享OkHttpClient捶箱,構(gòu)建Request,然后OkHttpClient根據(jù)Request構(gòu)建Call动漾,接著執(zhí)行call讼呢,最后進(jìn)行Response處理

同步請(qǐng)求

public class GetExample {
  OkHttpClient client = new OkHttpClient(); // 構(gòu)建共享的Client

  String run(String url) throws IOException {
    Request request = new Request.Builder() // 構(gòu)建request
        .url(url)
        .build();
    // 構(gòu)建Call,執(zhí)行
    try (Response response = client.newCall(request).execute()) {
      return response.body().string();
    }
  }

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
    System.out.println(response);
  }
}

異步請(qǐng)求

public final class AsynchronousGet {
  private final OkHttpClient client = new OkHttpClient();// 構(gòu)建共享Client

  public void run() throws Exception {
    // 構(gòu)建Request
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
    // 構(gòu)建Call谦炬,執(zhí)行悦屏,回調(diào)接受處理
    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody responseBody = response.body()) {
          if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

          Headers responseHeaders = response.headers();
          for (int i = 0, size = responseHeaders.size(); i < size; i++) {
            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
          }

          System.out.println(responseBody.string());
        }
      }
    });
  }

  public static void main(String... args) throws Exception {
    new AsynchronousGet().run();
  }
}

源碼分析

構(gòu)建OkHttpClient

  • OkHttpClient是Call的一個(gè)工廠類,OkHttpClient應(yīng)該是共享的键思,或者說(shuō)是單例
  • 可以通過(guò)newBuilder來(lái)自定義Client
  • 沒(méi)有必要關(guān)心 關(guān)閉和資源釋放
/*
 * Factory for [calls][Call], which can be used to send HTTP requests and read their responses.
 * ## OkHttpClients Should Be Shared
 * ## Customize Your Client With newBuilder()
 * ## Shutdown Isn't Necessary
 * /
open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
    // 3. 成員變量初始化
    ... 
    
    // 1. 內(nèi)部無(wú)參數(shù)構(gòu)造函數(shù)
    constructor() : this(Builder())
    
    // 4. OkHttpClient函數(shù)初始化 
    init {
        // 初始化證書(shū)和攔截器等判斷邏輯
        ...
    }
    // 2. Builder構(gòu)造函數(shù)
    class Builder constructor() {
        ...
    } 
}

OkHttpClient.Builder

Builder模式础爬,提供自定義配置化能力,同時(shí)有一份無(wú)需關(guān)心的默認(rèn)配置

  class Builder constructor() {
        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
    }   

OkHttpClient成員變量初始化

  • 初始化成員變量
  • JvmName是為了支持兼容3.x
  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

  @get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool

  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()

  @get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
      builder.networkInterceptors.toImmutableList()

  @get:JvmName("eventListenerFactory") val eventListenerFactory: EventListener.Factory =
      builder.eventListenerFactory

  @get:JvmName("retryOnConnectionFailure") val retryOnConnectionFailure: Boolean =
      builder.retryOnConnectionFailure

  @get:JvmName("cookieJar") val cookieJar: CookieJar = builder.cookieJar

  @get:JvmName("cache") val cache: Cache? = builder.cache

  ....

構(gòu)建Request

Request 對(duì)應(yīng)HTTP請(qǐng)求中的Request吼鳞,OkHttp依舊是Builder構(gòu)建模式構(gòu)建Request

Request.Builder

支持url看蚜、method、headers赔桌、body的配置

 open class Builder {
    internal var url: HttpUrl? = null 
    internal var method: String
    internal var headers: Headers.Builder
    internal var body: RequestBody? = null
    // 構(gòu)造Request
    open fun build(): Request {
      return Request(
          checkNotNull(url) { "url == null" },
          method,
          headers.build(),
          body,
          tags.toImmutableMap()
      )
    }
}

Request

通過(guò)Request.Builder構(gòu)造Request實(shí)例

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

構(gòu)建Call

OkHttpClient.newCall

OkHttpClient 實(shí)現(xiàn)了Call.Factory供炎,作為Call的構(gòu)造工廠類

  override fun newCall(request: Request): Call {
    // 執(zhí)行 RealCall的構(gòu)造call方法
    return RealCall.newRealCall(this, request, forWebSocket = false)
  }

RealCall.newRealCall

構(gòu)造Call真正方法,另外創(chuàng)建了一個(gè) 發(fā)射器疾党,接下來(lái)先了解下Call

companion object {
    fun newRealCall(
      client: OkHttpClient,
      originalRequest: Request,
      forWebSocket: Boolean
    ): RealCall {
      
      return RealCall(client, originalRequest, forWebSocket).apply {
       // 構(gòu)造了一個(gè) 發(fā)射器音诫,它是應(yīng)用層和網(wǎng)絡(luò)層交互的橋梁,后面會(huì)著重介紹
        transmitter = Transmitter(client, this) 
      }
    }
  }

RealCall

Call定義為一個(gè)準(zhǔn)備好執(zhí)行的請(qǐng)求雪位,它是能被取消的竭钝,且它只能被執(zhí)行一次(http請(qǐng)求也是一次執(zhí)行)
包括一個(gè)核心成員變量 Transmitter ,兩個(gè)重要方法 execute(同步) 和 enqueue(異步)


internal class RealCall private constructor(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
 // 發(fā)射機(jī)
 private lateinit var transmitter: Transmitter
 
 // 同步請(qǐng)求執(zhí)行方法
 override fun execute(): Response {
  ...
 }
  // 異步請(qǐng)求執(zhí)行方法 
  override fun enqueue(responseCallback: Callback) {
    ...
  }}
  
  // 取消請(qǐng)求
  override fun cancel() {
    transmitter.cancel()
  }

同步請(qǐng)求

RealCall.execute

  • 請(qǐng)求前校驗(yàn)邏輯雹洗,僅能執(zhí)行一次香罐,和過(guò)期時(shí)間邏輯判斷邏輯
  • 通知請(qǐng)求start事件,便于metrics 指標(biāo)數(shù)據(jù)收集
  • 將call加入分發(fā)器的同步請(qǐng)求隊(duì)列中
  • 通過(guò)攔截器責(zé)任鏈模式進(jìn)行請(qǐng)求和返回一系列邏輯處理
  override fun execute(): Response {
    // 檢查是否已經(jīng)執(zhí)行
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    // 過(guò)期時(shí)間邏輯时肿,如果配置了會(huì)有WatchDog線程進(jìn)行Watch然后執(zhí)行退出邏輯
    transmitter.timeoutEnter()
    // 通知start庇茫,最后會(huì)通過(guò) EventListener 發(fā)出時(shí)間,主要目的是收集 metrics events
    transmitter.callStart()
    try {
      // 調(diào)用client的dispatcher分發(fā)器執(zhí)行call(將call加入同步call隊(duì)列)
      client.dispatcher.executed(this)
      // 通過(guò)攔截器責(zé)任鏈模式進(jìn)行請(qǐng)求和返回處理等一系類邏輯
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }

RealCall.getResponseWithInterceptorChain

  • 配置攔截器螃成,所有請(qǐng)求和響應(yīng)處理邏輯解耦到各個(gè)攔截器負(fù)責(zé)模塊
  • 構(gòu)造攔截器鏈?zhǔn)秸{(diào)用處理類RealInterceptorChain實(shí)例
  • chain.proceed 進(jìn)行攔截器的鏈?zhǔn)秸{(diào)用
  fun getResponseWithInterceptorChain(): Response {
    // 構(gòu)建所有的攔截器
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors // client配置的攔截器
    interceptors += RetryAndFollowUpInterceptor(client) // 重試機(jī)制攔截器
    interceptors += BridgeInterceptor(client.cookieJar) // 請(qǐng)求和返回橋(http信息配置和解析)攔截器
    interceptors += CacheInterceptor(client.cache) // 緩存攔截
    interceptors += ConnectInterceptor // 連接攔截器
    if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)  // 執(zhí)行攔截器
    
    // 攔截器核心處理類 
    val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
        client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)

    var calledNoMoreExchanges = false
    try {
      // 鏈?zhǔn)秸{(diào)用
      val response = chain.proceed(originalRequest)
      if (transmitter.isCanceled) { // 處理取消
        response.closeQuietly()
        throw IOException("Canceled")
      }
      return response //返回請(qǐng)求結(jié)果
    } catch (e: IOException) {
      calledNoMoreExchanges = true
      throw transmitter.noMoreExchanges(e) as Throwable
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null)
      }
    }
  }

異步請(qǐng)求

RealCall.enqueue

  • 構(gòu)造一個(gè)異步call
  • 調(diào)用dispatcher 入隊(duì)AsyncCall
  override fun enqueue(responseCallback: Callback) {
    synchronized(this) { 
      check(!executed) { "Already Executed" }
      executed = true
    }
    transmitter.callStart()
    // 構(gòu)造 AsyncCall 旦签,接著分發(fā)器入隊(duì)操作
    client.dispatcher.enqueue(AsyncCall(responseCallback)) 
  }

Dispatcher 分發(fā)器隊(duì)Call進(jìn)行分發(fā)執(zhí)行

參考:線程池理解

  • 初始化時(shí)啥容,構(gòu)造分發(fā)器的線程池,及對(duì)應(yīng)執(zhí)行參數(shù)
  • 主要管理異步請(qǐng)求的處理(異步Call入隊(duì)顷霹、并發(fā)執(zhí)行)
class Dispatcher constructor() {
  // 默認(rèn)最大請(qǐng)求數(shù) 64
  @get:Synchronized var maxRequests = 64
  // 默認(rèn)最大并發(fā)Host 5
  @get:Synchronized var maxRequestsPerHost = 5
  // 線程池執(zhí)行器 默認(rèn)創(chuàng)建可緩存線程池 
  @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!!
    }
    
  /** 準(zhǔn)備好的異步Call對(duì)列 */
  private val readyAsyncCalls = ArrayDeque<AsyncCall>()

  /** 執(zhí)行中異步call對(duì)列 */
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()

  /** 同步call對(duì)列 */
  private val runningSyncCalls = ArrayDeque<RealCall>()
  
  // 異步Call 入隊(duì)操作  
  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)
      // same host情況下的復(fù)用邏輯處理
      if (!call.get().forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host())
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute() // 準(zhǔn)備線程池執(zhí)行器咪惠,及執(zhí)行
  }

// 執(zhí)行Call
private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      // 遍歷所有的ready 異步 call
      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) // 加入此次執(zhí)行隊(duì)列緩存
        runningAsyncCalls.add(asyncCall) // 加入正在執(zhí)行隊(duì)列
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService) // 將線程執(zhí)行器傳入call,在Call中進(jìn)行執(zhí)行
    }

    return isRunning
  }
  
  /** 同步call淋淀,添加到隊(duì)列 */
  @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

}

AsyncCall執(zhí)行

  • executeOn 遥昧,線程池執(zhí)行器執(zhí)行Runnable
  • run,通過(guò)攔截器進(jìn)行請(qǐng)求和獲取響應(yīng)結(jié)果
fun executeOn(executorService: ExecutorService) {
    client.dispatcher.assertThreadDoesntHoldLock()
    
    var success = false
    try {
        // 線程池 執(zhí)行器執(zhí)行
        executorService.execute(this)
        success = true
    }
}
// 執(zhí)行方法
override fun run() {
    threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        transmitter.timeoutEnter()
        try {
            // 同“同步請(qǐng)求” 最終執(zhí)行攔截器鏈?zhǔn)秸{(diào)用
            val response = getResponseWithInterceptorChain()
            signalledCallback = true
            // 響應(yīng) 回調(diào)
            responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
            ...
        } finally {
            client.dispatcher.finished(this)
        }
    }
}

下一篇 OkHttp 4源碼(2)— 攔截器機(jī)制分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末朵纷,一起剝皮案震驚了整個(gè)濱河市炭臭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袍辞,老刑警劉巖鞋仍,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搅吁,居然都是意外死亡威创,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門谎懦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肚豺,“玉大人,你說(shuō)我怎么就攤上這事界拦∥辏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵享甸,是天一觀的道長(zhǎng)截碴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蛉威,這世上最難降的妖魔是什么日丹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瓷翻,結(jié)果婚禮上聚凹,老公的妹妹穿的比我還像新娘割坠。我一直安慰自己齐帚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布彼哼。 她就那樣靜靜地躺著对妄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敢朱。 梳的紋絲不亂的頭發(fā)上剪菱,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天摩瞎,我揣著相機(jī)與錄音,去河邊找鬼孝常。 笑死旗们,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的构灸。 我是一名探鬼主播上渴,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喜颁!你這毒婦竟也來(lái)了稠氮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤半开,失蹤者是張志新(化名)和其女友劉穎隔披,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寂拆,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奢米,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纠永。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恃慧。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖渺蒿,靈堂內(nèi)的尸體忽然破棺而出痢士,到底是詐尸還是另有隱情,我是刑警寧澤茂装,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布怠蹂,位于F島的核電站,受9級(jí)特大地震影響少态,放射性物質(zhì)發(fā)生泄漏城侧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一彼妻、第九天 我趴在偏房一處隱蔽的房頂上張望嫌佑。 院中可真熱鬧古话,春花似錦蜀踏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炮温。三九已至,卻和暖如春牵舵,著一層夾襖步出監(jiān)牢的瞬間柒啤,已是汗流浹背倦挂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留担巩,地道東北人方援。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涛癌,于是被迫代替她去往敵國(guó)和親肯骇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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