網(wǎng)絡(luò)請(qǐng)求框架-OkHttp原理解析

OKHttp理解

okhttp是square公司貢獻(xiàn)的一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開(kāi)源框架越锈,是目前Android開(kāi)發(fā)使用最廣泛的一個(gè)網(wǎng)絡(luò)框架烤咧,從Android4.4開(kāi)始酬荞,httpURLconnection的底層實(shí)現(xiàn)采用的就是okhttp。內(nèi)部實(shí)現(xiàn)就是利用java基礎(chǔ)塑荒,對(duì)socket進(jìn)行封裝忽妒,實(shí)現(xiàn)http通信玩裙。最重要的兩個(gè)關(guān)鍵點(diǎn)就是分發(fā)器和5個(gè)攔截器。
分發(fā)器就是內(nèi)部維護(hù)隊(duì)列和線程池段直,完成請(qǐng)求分配吃溅,總結(jié)就是用于對(duì)異步任務(wù)加入隊(duì)列管理,然后判斷條件鸯檬,控制數(shù)量决侈,加入線程池執(zhí)行異步請(qǐng)求任務(wù)。
五個(gè)默認(rèn)攔截器就是利用責(zé)任鏈模式對(duì)網(wǎng)絡(luò)請(qǐng)求進(jìn)行層層處理喧务,完成整個(gè)請(qǐng)求過(guò)程颜及,簡(jiǎn)單總結(jié)如下。
1.橋接攔截器對(duì)用戶發(fā)出的請(qǐng)求添加缺少的請(qǐng)求配置字段蹂楣,比如keep-alive等
2.緩存攔截器就是查詢有沒(méi)有符合判斷條件的已緩存的網(wǎng)絡(luò)請(qǐng)求,執(zhí)行復(fù)用讯蒲,直接返回response
3.連接攔截器就是創(chuàng)建請(qǐng)求痊土,加入連接器 或者訪問(wèn)連接池,根據(jù)條件判斷墨林,是否能懟已創(chuàng)建的tcp請(qǐng)求進(jìn)行復(fù)用
4.請(qǐng)求服務(wù)器攔截器就是對(duì)scoket進(jìn)行操作赁酝,請(qǐng)求網(wǎng)絡(luò)訪問(wèn)服務(wù)器犯祠,返回response,
5.重試和重定向攔截器就是對(duì)返回的response進(jìn)行code判斷酌呆,決定是否要重試或者重定向操作衡载。

okhttp的優(yōu)點(diǎn)

1.支持http2.0版本,并且允許對(duì)同一主機(jī)的所有請(qǐng)求共享一個(gè)套接字
2.即使不是http2.0版本隙袁,通過(guò)連接池痰娱,減少請(qǐng)求延遲
3.默認(rèn)使用Gzip 壓縮數(shù)據(jù)
4.響應(yīng)緩存,避免重復(fù)請(qǐng)求網(wǎng)絡(luò)

okHttp的執(zhí)行流程

最簡(jiǎn)單的http請(qǐng)求案例

 val client = OkHttpClient.Builder().build()
        val request= Request.Builder().url("https://www.baidu.com").build()
        //異步請(qǐng)求
        client.newCall(request).enqueue(object :Callback{
            override fun onFailure(call: Call, e: IOException) {
            }

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

        })
        //同步請(qǐng)求
        client.newCall(request).execute();
1111.png

1.利用建造者模式構(gòu)建okHttpClient實(shí)例對(duì)象,構(gòu)建過(guò)程中可以動(dòng)態(tài)配置參數(shù)菩收,請(qǐng)求時(shí)間梨睁,響應(yīng)時(shí)間,緩存信息等娜饵。
2.創(chuàng)建Request對(duì)象坡贺,設(shè)置請(qǐng)求方式,鏈接地址箱舞,參數(shù)等信息遍坟。
3.把request對(duì)象,傳給client晴股,通過(guò)newCall函數(shù)愿伴,得到RealCall對(duì)象。

override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

4.RealCall 分為同步和異步執(zhí)行
5.同步執(zhí)行時(shí)队魏,分發(fā)器只是做個(gè)記錄公般,把請(qǐng)求任務(wù)加到隊(duì)列中,然后直接通過(guò)攔截器訪問(wèn)服務(wù)器胡桨,返回response官帘。

  override fun execute(): Response {
  ....
    try {
      client.dispatcher.executed(this)//分發(fā)器記錄任務(wù)  runningSyncCalls.add(call)
      return getResponseWithInterceptorChain() //返回請(qǐng)求結(jié)果 response
    } finally {
      client.dispatcher.finished(this)
    }
  }

6.異步執(zhí)行
6.1先對(duì)異步任務(wù)進(jìn)一步封裝,把任務(wù)放到AsyncCall對(duì)象中

  override fun enqueue(responseCallback: Callback) {
  ...
    client.dispatcher.enqueue(AsyncCall(responseCallback))
//AsyncCall(responseCallback)對(duì)異步任務(wù)進(jìn)一步封裝
  }

internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable

2.分發(fā)器 把 封裝后的異步任務(wù) 添加到等待運(yùn)行的隊(duì)列中

======分發(fā)器維護(hù)的三個(gè)隊(duì)列======
//等待運(yùn)行的異步任務(wù)隊(duì)列
 private val readyAsyncCalls = ArrayDeque<AsyncCall>()

//正在運(yùn)行的異步任務(wù)隊(duì)列
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()

//同步任務(wù)隊(duì)列
  private val runningSyncCalls = ArrayDeque<RealCall>()

//異步任務(wù)昧谊,加入隊(duì)列
  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)

      if (!call.call.forWebSocket) {
        //在等待隊(duì)列或者運(yùn)行隊(duì)列中找出有沒(méi)有相同host的請(qǐng)求任務(wù)
        val existingCall = findExistingCallWithHost(call.host)
        //如果有相同host的請(qǐng)求刽虹,那么就把他們的callsPerHost 設(shè)置為同一個(gè)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
 //執(zhí)行分發(fā)操作
    promoteAndExecute()
  }

=======syncCall 字段說(shuō)明==================
//這個(gè)字段用來(lái)設(shè)置 當(dāng)前 正在執(zhí)行的任務(wù),有多少個(gè)是相同host
 @Volatile var callsPerHost = AtomicInteger(0)

fun reuseCallsPerHostFrom(other: AsyncCall) {
      this.callsPerHost = other.callsPerHost
    }
========end=======================

//分發(fā)操作
private fun promoteAndExecute(): Boolean {
    
  //臨時(shí)容器
    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
       //遍歷等待運(yùn)行的異步任務(wù)隊(duì)列
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
        
        //正在執(zhí)行的異步任務(wù)數(shù)量 不能大于默認(rèn)最大數(shù)量64
        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
         // 同一個(gè)時(shí)刻呢诬,對(duì)同一個(gè)host的訪問(wèn)數(shù)量涌哲,不能大于5個(gè)
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
        //刪除等待隊(duì)列的異步任務(wù)
        i.remove()
        //設(shè)置任務(wù)主機(jī)Host 的數(shù)量
        asyncCall.callsPerHost.incrementAndGet()
      //把任務(wù)加入臨時(shí)容器
        executableCalls.add(asyncCall)
      //把任務(wù)加入正在運(yùn)行的隊(duì)列
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      //利用線程池,執(zhí)行異步任務(wù)
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

====線程池的配置======
okHttp 配置的線程池尚镰,等同于jdk提供的newCacheThreadPool
核心線程的數(shù)量為0阀圾,最大線程數(shù)量不限制,阻塞隊(duì)列不能存儲(chǔ)元素狗唉。
這樣配置的好處就是可以并發(fā)執(zhí)行多個(gè)異步任務(wù)初烘。如果阻塞隊(duì)列能添加元素,比如LinkedBlockingQueue 只有等上個(gè)任務(wù)結(jié)束之后,才能執(zhí)行下個(gè)任務(wù)肾筐。
val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
      }
      return executorServiceOrNull!!
    }
=======end=======
//執(zhí)行異步任務(wù)
  fun executeOn(executorService: ExecutorService) {
   ....
      try {
        executorService.execute(this) //執(zhí)行syncCall的run函數(shù)
        success = true
      } catch (e: RejectedExecutionException) {
       .....
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

//執(zhí)行子線程任務(wù)
override fun run() {
         ......
        try {
          //利用攔截器哆料,請(qǐng)求網(wǎng)絡(luò),分發(fā)器任務(wù)結(jié)束
          val response = getResponseWithInterceptorChain()
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
           ......
        } finally {
          client.dispatcher.finished(this)
        }
      }
    }

=====同步或者異步吗铐,最終都會(huì)調(diào)用 client.dispatcher.finished(this)======
internal fun finished(call: RealCall) {//同步任務(wù)結(jié)束
    finished(runningSyncCalls, call)
  }
 internal fun finished(call: AsyncCall) {//異步任務(wù)結(jié)束
    call.callsPerHost.decrementAndGet() //對(duì)相同主機(jī)數(shù)量 進(jìn)行-1操作
    finished(runningAsyncCalls, call)
  }

private fun <T> finished(calls: Deque<T>, call: T) {
       ....
    val isRunning = promoteAndExecute() //再次去分發(fā)異步任務(wù)
    ....
  }
=======end======

2222.png

7.getResponseWithInterceptorChain 通過(guò)攔截器东亦,獲取response
okhttp 默認(rèn)提供5個(gè)攔截器 重試重定向攔截器,橋接攔截器唬渗,緩存攔截器典阵,連接攔截器,訪問(wèn)服務(wù)器攔截器谣妻。還可以自定義攔截器萄喳。
自定義攔截器分為應(yīng)用攔截器(通過(guò)addInterceptor 添加)和網(wǎng)絡(luò)攔截器(通過(guò)addNetworkInterceptor攔截)

=====添加自定義攔截器=========
val client = OkHttpClient.Builder()
            .addInterceptor (object :Interceptor{ //添加應(yīng)用攔截器
                override fun intercept(chain: Interceptor.Chain): Response {
                   val request = chain.request()
                    val newRequest =           request.newBuilder().addHeader("token","11111111").build()
                    return chain.proceed(newRequest)
                }
            })
            .addNetworkInterceptor(object :Interceptor{//添加網(wǎng)絡(luò)攔截器
                override fun intercept(chain: Interceptor.Chain): Response {
                      省略一些攔截操作 xxxxxxxx
                    return chain.proceed(chain.request())
                }

            })
            .build()

=========end========
 internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors //添加應(yīng)用攔截器
    interceptors += RetryAndFollowUpInterceptor(client) //重試重定向
    interceptors += BridgeInterceptor(client.cookieJar) //橋接
    interceptors += CacheInterceptor(client.cache) //緩存攔截器
    interceptors += ConnectInterceptor //連接攔截器
    if (!forWebSocket) {
      interceptors += client.networkInterceptors //網(wǎng)絡(luò)攔截器
    }
    interceptors += CallServerInterceptor(forWebSocket) //訪問(wèn)服務(wù)攔截器

   //構(gòu)建 鏈接對(duì)象
    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 {
      //經(jīng)過(guò)攔截器  響應(yīng)請(qǐng)求,返回response
      val response = chain.proceed(originalRequest)
      ....
      return response
    } catch (e: IOException) {
      ......
    }
  }
339.png

okhttp 攔截器原理解析

攔截器采用責(zé)任鏈的設(shè)計(jì)默認(rèn)蹋半,讓請(qǐng)求者和處理者解耦他巨,最終請(qǐng)求從前往后,響應(yīng)從后往前减江。


558.png

6650.png

重試重定向攔截器 RetryAndFollowUpInterceptor

首先先判斷用戶是否取消了請(qǐng)求染突,如果沒(méi)有取消,就把請(qǐng)求交個(gè)橋接攔截器辈灼。
在獲得響應(yīng)結(jié)果response的時(shí)候根據(jù)響應(yīng)碼份企,判斷是否需要重試或者重定向,重試不限制次數(shù)巡莹,重定向最多20次司志,如果需要重試或者重定向,那么會(huì)再一次重新執(zhí)行所有攔截器降宅。
有如下幾種情況不會(huì)重試:IO異常骂远,線路異常,配置client實(shí)例時(shí)配置不允許重試腰根,協(xié)議異常激才,證書(shū)異常等等。

619.png

判斷是否允許重定向使用返回的response的code碼決定的额嘿,或者在構(gòu)建client是設(shè)置是否允許重定向 followRegirects(默認(rèn)為true)
037.png

橋接攔截器 BridgeInterceptor

先獲取用戶發(fā)送的請(qǐng)求瘸恼,判斷條件用戶是否已經(jīng)配置過(guò)請(qǐng)求頭字段,若用戶沒(méi)有配置册养,則將http協(xié)議必備的請(qǐng)求頭字段補(bǔ)齊东帅,比如Content-Type,Content-Length等球拦,然后交給下一個(gè)攔截器靠闭。
在獲得響應(yīng)結(jié)果response之后邓夕,調(diào)用保存cookie的接口(也可以在配置client的時(shí)候,設(shè)置cookjar進(jìn)行cookie回調(diào)數(shù)據(jù))阎毅,并且解析gzip數(shù)據(jù)

class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val userRequest = chain.request()
    val requestBuilder = userRequest.newBuilder()

    val body = userRequest.body
    if (body != null) {
      val contentType = body.contentType()
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString())
      }

      val contentLength = body.contentLength()
      if (contentLength != -1L) {
        requestBuilder.header("Content-Length", contentLength.toString())
        requestBuilder.removeHeader("Transfer-Encoding")
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked")
        requestBuilder.removeHeader("Content-Length")
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", userRequest.url.toHostHeader())
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive")
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true
      requestBuilder.header("Accept-Encoding", "gzip")
    }

    val cookies = cookieJar.loadForRequest(userRequest.url)
    if (cookies.isNotEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies))
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", userAgent)
    }

獲取結(jié)果之后,對(duì)cookie進(jìn)行保存点弯,對(duì)返回的數(shù)據(jù)進(jìn)行g(shù)zip解壓

 val networkResponse = chain.proceed(requestBuilder.build())

    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)

    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      val responseBody = networkResponse.body
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        val strippedHeaders = networkResponse.headers.newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()
        responseBuilder.headers(strippedHeaders)
        val contentType = networkResponse.header("Content-Type")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
0451.png

緩存攔截器CacheInterceptor

就是根據(jù)緩存策略從緩存中查找是否有合適的緩存response扇调,如果有合適的緩存,直接返回給請(qǐng)求任務(wù)抢肛,不在繼續(xù)執(zhí)行后面的攔截器狼钮。
獲得響應(yīng)結(jié)果response后,根據(jù)條件判斷捡絮,決定是否要緩存熬芜。

class CacheInterceptor(internal val cache: Cache?) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val call = chain.call()
    val cacheCandidate = cache?.get(chain.request())

    val now = System.currentTimeMillis()

    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

11423.png
1352.png

連接攔截器ConnectInterceptor

維護(hù)一個(gè)連接池,負(fù)責(zé)對(duì)連接的服務(wù)福稳。在把請(qǐng)求交給下一個(gè)攔截器之前涎拉。會(huì)先在連接池中找到一個(gè)合適的連接(滿足適配條件相同,并且沒(méi)有正在被使用)或者新建一個(gè)連接的圆,并且接入連接池鼓拧,獲得對(duì)應(yīng)的socket流,把請(qǐng)求交給下一個(gè)攔截器越妈。獲得response結(jié)果后不會(huì)進(jìn)行額外的處理季俩。

object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)//入口處
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }
}
 internal fun initExchange(chain: RealInterceptorChain): Exchange {
  ....
    val exchangeFinder = this.exchangeFinder!!
    val codec = exchangeFinder.find(client, chain) //查找連接池
    val result = Exchange(this, eventListener, exchangeFinder, codec)
   ....
  }

  fun find(
    client: OkHttpClient,
    chain: RealInterceptorChain
  ): ExchangeCodec {
    try {
      val resultConnection = findHealthyConnection(
        ....
      )
      return resultConnection.newCodec(client, chain)
    } ...
  }

 private fun findHealthyConnection(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean,
    doExtensiveHealthChecks: Boolean
  ): RealConnection {
    while (true) {
      val candidate = findConnection(...  )//攜帶信息,查找連接池.
  .....
}
======RealConnection 判斷復(fù)用條件========================
internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
    assertThreadHoldsLock()

    // 判斷這個(gè)連接是否正在被使用
    if (calls.size >= allocationLimit || noNewExchanges) return false

 //判斷基礎(chǔ)信息是否相同 證書(shū)梅掠,協(xié)議酌住,url,ssl等
    if (!this.route.address.equalsNonHost(address)) return false 

    // 請(qǐng)的host也必須一直
    if (address.url.host == this.route().address.url.host) {
      return true // This connection is a perfect match.
    }
===必須跟下面的信息相同的請(qǐng)求才能復(fù)用 this.route.address.equalsNonHost(address)=================
 internal fun equalsNonHost(that: Address): Boolean {
    return this.dns == that.dns &&
        this.proxyAuthenticator == that.proxyAuthenticator &&
        this.protocols == that.protocols &&
        this.connectionSpecs == that.connectionSpecs &&
        this.proxySelector == that.proxySelector &&
        this.proxy == that.proxy &&
        this.sslSocketFactory == that.sslSocketFactory &&
        this.hostnameVerifier == that.hostnameVerifier &&
        this.certificatePinner == that.certificatePinner &&
        this.url.port == that.url.port
  }

連接池阎抒,也稱之為對(duì)象池酪我,主要用來(lái)存放request請(qǐng)求連接,內(nèi)部維護(hù)了一個(gè)LinkedQueue隊(duì)列用來(lái)存放請(qǐng)求挠蛉。在添加新的請(qǐng)求對(duì)象時(shí)祭示,都會(huì)執(zhí)行一個(gè)周期性任務(wù),用以對(duì)連接池進(jìn)行清理操作谴古。
1.隊(duì)列長(zhǎng)度超過(guò)5质涛,清理最近未被使用連接,LRE算法
2.存儲(chǔ)的連接掰担,5分鐘未被復(fù)用汇陆,清理

====RealConnectionPool 實(shí)例化====
class ConnectionPool internal constructor(
  internal val delegate: RealConnectionPool
) {
  constructor(
    maxIdleConnections: Int,
    keepAliveDuration: Long,
    timeUnit: TimeUnit
  ) : this(RealConnectionPool(//this(5, 5, TimeUnit.MINUTES)
      taskRunner = TaskRunner.INSTANCE,
      maxIdleConnections = maxIdleConnections,  
      keepAliveDuration = keepAliveDuration,
      timeUnit = timeUnit
  ))

  constructor() : this(5, 5, TimeUnit.MINUTES)
======end===========



class RealConnectionPool(
  taskRunner: TaskRunner,
  /** The maximum number of idle connections for each address. */
  private val maxIdleConnections: Int, //隊(duì)列的長(zhǎng)度-- 5
  keepAliveDuration: Long, //休閑請(qǐng)求的存活時(shí)間 ---5
  timeUnit: TimeUnit//單位 TimeUnit.MINUTES
)  {
//周期性的清理任務(wù)
  private val cleanupTask = object : Task("$okHttpName ConnectionPool") {
    override fun runOnce() = cleanup(System.nanoTime())
    1.隊(duì)列長(zhǎng)度超過(guò)5,清理最近未被使用request LRE算法
    2.空閑請(qǐng)求带饱,5分鐘未被復(fù)用毡代,清理
  }

//隊(duì)列保存請(qǐng)求對(duì)象
  private val connections = ConcurrentLinkedQueue<RealConnection>()

//保存請(qǐng)求對(duì)象阅羹,開(kāi)啟周期性任務(wù)
 fun put(connection: RealConnection) {
    connection.assertThreadHoldsLock()

    connections.add(connection)
    cleanupQueue.schedule(cleanupTask)
  }

1420.png

訪問(wèn)服務(wù)攔截器CallServerInterceptor

拿到上一個(gè)攔截器返回的請(qǐng)求,真正的與服務(wù)器進(jìn)行通信教寂,向服務(wù)器發(fā)送數(shù)據(jù)捏鱼,解析讀取響應(yīng)的數(shù)據(jù),返回給上一個(gè)攔截器酪耕。

okhttp 通信流程總結(jié)

999.png

okhttp 面試題總結(jié)

OkHttp 請(qǐng)求的整體流程

1.創(chuàng)建request =>OkHttpClient=>RealCall()
2.同步執(zhí)行 导梆,分發(fā)器添加同步任務(wù),執(zhí)行攔截器迂烁,訪問(wèn)服務(wù)器看尼,返回reponse,觸發(fā)異步分發(fā)流程盟步。
3.異步執(zhí)行 藏斩,封裝任務(wù)= >AsyncCall ,實(shí)現(xiàn)runnable接口却盘。添加任務(wù)到異步任務(wù)等待隊(duì)列狰域,執(zhí)行分發(fā)任務(wù),判斷異步任務(wù)是否能加入正在執(zhí)行的異步任務(wù)隊(duì)列谷炸,滿足兩個(gè)條件
同時(shí)執(zhí)行的異步任務(wù)數(shù)量不得大于64個(gè)
對(duì)同一個(gè)主機(jī)的訪問(wèn)任務(wù)北专,最多不得大于5個(gè)
4.加入正在執(zhí)行的異步任務(wù)隊(duì)列,通過(guò)線程池執(zhí)行任務(wù)旬陡,經(jīng)過(guò)5個(gè)默認(rèn)攔截器訪問(wèn)服務(wù)器拓颓,返回response,執(zhí)行異步任務(wù)分發(fā)描孟。

分發(fā)器是如何工作的

分發(fā)器工作 分為同步任務(wù)和異步任務(wù)兩種
同步任務(wù) 就是把任務(wù)加入同步任務(wù)隊(duì)列驶睦,加個(gè)標(biāo)記,執(zhí)行結(jié)束之后匿醒,觸發(fā)異步任務(wù)的分發(fā)操作场航。
異步任務(wù) 先封裝任務(wù)到asyncCall對(duì)象,實(shí)現(xiàn)了runnable接口廉羔。把任務(wù)加入等待執(zhí)行隊(duì)列溉痢,執(zhí)行分發(fā)操作。
先遍歷等待任務(wù)隊(duì)列憋他,判斷是否符合加入正在運(yùn)行的異步任務(wù)隊(duì)列孩饼,要同時(shí)滿足兩個(gè)條件。
同時(shí)執(zhí)行的異步任務(wù)數(shù)量不得大于64個(gè)
對(duì)同一個(gè)主機(jī)的訪問(wèn)任務(wù)竹挡,最多不得大于5個(gè)
當(dāng)滿足條件后镀娶,從等待隊(duì)列中刪除任務(wù),把任務(wù)加入正在執(zhí)行的隊(duì)列中揪罕,通過(guò)自定義的線程池梯码,執(zhí)行任務(wù)宝泵,任務(wù)執(zhí)行結(jié)束后,再次執(zhí)行分發(fā)操作轩娶。

攔截器是如何工作的

攔截器采用了責(zé)任鏈設(shè)計(jì)默認(rèn)儿奶,讓請(qǐng)求者和執(zhí)行者解耦,請(qǐng)求者只需要將請(qǐng)求發(fā)給責(zé)任鏈即可鳄抒,無(wú)需關(guān)心請(qǐng)求過(guò)程和細(xì)節(jié)廓握。okHttp 默認(rèn)有5個(gè)攔截器,重試重定向攔截器嘁酿,橋接攔截器,緩存攔截器男应,連接攔截器闹司,請(qǐng)求服務(wù)攔截器。工作細(xì)節(jié)參考上面攔截器原理分析部分


6650.png

應(yīng)用攔截器和網(wǎng)絡(luò)攔截器的區(qū)別

1.位置的關(guān)系沐飘,應(yīng)用攔截器 放在責(zé)任鏈最頂端游桩,網(wǎng)絡(luò)攔截器放在責(zé)任鏈倒數(shù)第二的位置。所以應(yīng)用攔截器 最先攔截耐朴,最后響應(yīng)借卧,網(wǎng)絡(luò)攔截器 倒數(shù)第二攔截,第二響應(yīng)筛峭。如果打印請(qǐng)求日志的情況铐刘,應(yīng)用攔截器打印的是用戶請(qǐng)求信息,經(jīng)過(guò)重試重定向影晓,橋接镰吵,緩存,鏈接 等攔截器的層層包裝挂签,網(wǎng)絡(luò)攔截器打印的是實(shí)際請(qǐng)求的信息疤祭。
2.應(yīng)用攔截器一定會(huì)被執(zhí)行,網(wǎng)絡(luò)攔截器不一定被執(zhí)行饵婆。

okHttp 如何復(fù)用TCP連接

利用連接池勺馆,緩存所有的有效連接對(duì)象。
清理機(jī)制:垃圾連接
1.超過(guò)5分鐘沒(méi)有用過(guò)的鏈接
2.超過(guò)5個(gè)閑置鏈接后侨核,從最久閑置的鏈接開(kāi)始執(zhí)行清理(LRU)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末草穆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芹关,更是在濱河造成了極大的恐慌续挟,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥衬,死亡現(xiàn)場(chǎng)離奇詭異诗祸,居然都是意外死亡跑芳,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)直颅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)博个,“玉大人,你說(shuō)我怎么就攤上這事功偿∨栌叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵械荷,是天一觀的道長(zhǎng)共耍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)吨瞎,這世上最難降的妖魔是什么痹兜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮颤诀,結(jié)果婚禮上字旭,老公的妹妹穿的比我還像新娘。我一直安慰自己崖叫,他們只是感情好遗淳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著心傀,像睡著了一般屈暗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脂男,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天恐锦,我揣著相機(jī)與錄音,去河邊找鬼疆液。 笑死一铅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堕油。 我是一名探鬼主播潘飘,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掉缺!你這毒婦竟也來(lái)了卜录?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤眶明,失蹤者是張志新(化名)和其女友劉穎艰毒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體搜囱,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丑瞧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年柑土,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绊汹。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稽屏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出西乖,到底是詐尸還是另有隱情狐榔,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布获雕,位于F島的核電站薄腻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏届案。R本人自食惡果不足惜被廓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萝玷。 院中可真熱鬧,春花似錦昆婿、人聲如沸球碉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)睁冬。三九已至,卻和暖如春看疙,著一層夾襖步出監(jiān)牢的瞬間豆拨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工能庆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留施禾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓搁胆,卻偏偏與公主長(zhǎng)得像弥搞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渠旁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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