IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> OkHttp -> 正文阅读

[移动开发]OkHttp

1 OkHttp简介

目前主流的Android网络请求框架有OkHttpRetrofit,不过,Retrofit底层使用的是OKHttp,其自身是不具备网络请求能力的。

OkHttp是由Square公司开发并共享开源的高效网络访问框架,使用简单,它替代了HttpUrlConnectionApacheHttpClient

默认情况下OkHttp具备以下特性:

  • 支持HTTP/2,允许同一个主机地址的所有请求共享同一个Socket连接
  • 连接池减少请求延时
  • 透明的GZIP压缩较少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求
  • 网络出现问题后,OkHttp保持不变,自动从问题中恢复

下面是Http常见的一些状态码:

  • 100~199:指示信息,表示请求已接收,继续处理;
  • 200~299:请求成功,表示请求已被成功接收、理解;
  • 300~399:重定向,要完成请求必须进行更进一步的操作;
  • 400~499:客户端错误,请求有语法错误或请求无法实现;
  • 500~599:服务器端错误,服务器未能实现合法的请求;

2 OkHttp请求流程

以下是OkHttp发起请求的大致流程:

OKHttp的请求流程

在使用OkHttp进行请求时,首先要创建一个OkHttpClient的实例:

val client = OkHttpClient()

如果想要发起一条HTTP请求,就需要创建一个Request对象:

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

之后调用OkHttpClientnewCall方法来创建一个Call对象,并调用它的execute/enqueue方法来发送请求并获取服务器返回的数据,其中,execute()方法是同步方法,enqueue()方法是异步方法:

val response = client.newCall(request).execute()

response对象就是服务器返回的数据,可以使用如下写法来的到返回的具体内容:

val responseData = response.body?.string()

下面是OkHttp进行GET请求/POST请求的代码:

// GET请求
private val client = OkHttpClient() // 新建OkHttpClient客户端
fun getMethod(url: String): String {
    val request = Request.Builder().url(url).build() // 新建Request对象
    val response = client.newCall(request).execute() // Response为OkHttp中的响应
    return response.body?.string() ?: ""
}

// POST请求
val JSON = "application/json; charset=utf-8".toMediaType()
fun postMethod(url: String, json: String): String {
   val body = RequestBody.create(JSON, json)
   val request = Request.Builder().url(url).post(body).build()
   val response = client.newCall(request).execute()
   return response.body?.string() ?: ""
}

2.1 OkHttpClient

在使用OkHttpClient之前,需要先创建一个OkHttpClient客户端,OkHttpClient的构造方法如下:

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {  
  constructor() : this(Builder())
}

可以看到,OkHttpClient使用了建造者模式,Builder里面的可配置参数如下:

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {  
  constructor() : this(Builder())
  
  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 // 安全套接层socket,工厂,用于HTTPS
    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 // 验证确认响应证书,适用HTTPS请求连接的主机
    internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
    internal var certificateChainCleaner: CertificateChainCleaner? = null // 验证确认响应证书,适用HTTPS请求连接的主机名
    internal var callTimeout = 0
    internal var connectTimeout = 10_000
    internal var readTimeout = 10_000
    internal var writeTimeout = 10_000
    internal var pingInterval = 0
    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
    internal var routeDatabase: RouteDatabase? = null

    internal constructor(okHttpClient: OkHttpClient) : this() {
      this.dispatcher = okHttpClient.dispatcher
      this.connectionPool = okHttpClient.connectionPool
      this.interceptors += okHttpClient.interceptors
      this.networkInterceptors += okHttpClient.networkInterceptors
      this.eventListenerFactory = okHttpClient.eventListenerFactory
      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
      this.authenticator = okHttpClient.authenticator
      this.followRedirects = okHttpClient.followRedirects
      this.followSslRedirects = okHttpClient.followSslRedirects
      this.cookieJar = okHttpClient.cookieJar
      this.cache = okHttpClient.cache
      this.dns = okHttpClient.dns
      this.proxy = okHttpClient.proxy
      this.proxySelector = okHttpClient.proxySelector
      this.proxyAuthenticator = okHttpClient.proxyAuthenticator
      this.socketFactory = okHttpClient.socketFactory
      this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
      this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
      this.connectionSpecs = okHttpClient.connectionSpecs
      this.protocols = okHttpClient.protocols
      this.hostnameVerifier = okHttpClient.hostnameVerifier
      this.certificatePinner = okHttpClient.certificatePinner
      this.certificateChainCleaner = okHttpClient.certificateChainCleaner
      this.callTimeout = okHttpClient.callTimeoutMillis
      this.connectTimeout = okHttpClient.connectTimeoutMillis
      this.readTimeout = okHttpClient.readTimeoutMillis
      this.writeTimeout = okHttpClient.writeTimeoutMillis
      this.pingInterval = okHttpClient.pingIntervalMillis
      this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
      this.routeDatabase = okHttpClient.routeDatabase
    } 
  }
}

2.2 同步请求

同步请求是指发出网络请求之后当前线程被阻塞,直到请求的结果(成功或者失败)到来,才继续向下执行。同步请求使用的是execute方法,如下所示:

val response = client.newCall(request).execute()

以下是源码:

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

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

class RealCall( // RealCall为真正的请求执行者
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
  internal val eventListener: EventListener = client.eventListenerFactory.create(this)

  val call: RealCall
  			get() = this@RealCall

  override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" } // 每个call只能执行一次

    timeout.enter()
    callStart()
    try {
      client.dispatcher.executed(this) // 通过dispatcher已经进入执行状态
      return getResponseWithInterceptorChain() // 通过一系列的拦截器请求处理和响应处理得到最终的返回结果
    } finally {
      client.dispatcher.finished(this) // 通知dispatcher,已经执行完毕
    }
  }
  
   @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors // 在配置OkHttpClient时设置的interceptors
    interceptors += RetryAndFollowUpInterceptor(client) // 负责失败重试以及重定向
    interceptors += BridgeInterceptor(client.cookieJar) // 请求时,对必要的Header进行一些添加,接收响应时,移除非必要的Header
    interceptors += CacheInterceptor(client.cache) // 负责读取缓存直接返回、更新缓存
    interceptors += ConnectInterceptor // 负责和服务器建立连接
    if (!forWebSocket) {
      interceptors += client.networkInterceptors // 配置OkHttpClient时设置的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)
      }
    }
  }
}

class RealInterceptorChain(
  internal val call: RealCall,
  private val interceptors: List<Interceptor>,
  private val index: Int,
  internal val exchange: Exchange?,
  internal val request: Request,
  internal val connectTimeoutMillis: Int,
  internal val readTimeoutMillis: Int,
  internal val writeTimeoutMillis: Int
) : Interceptor.Chain {

  @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.
    // 实例化下一个拦截器对应的RealInterceptorChain对象
    val next = copy(index = index + 1, request = request)
    // 得到当前的拦截器
    val interceptor = interceptors[index]

    // 调用当前拦截器的intercept()方法,并将洗一个拦截器的RealInterceptorChain对象传递下去,最后得到响应
    @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
  }
}

同步请求交给了RealCall类,是RealCall类执行了execute方法。

2.3 异步请求

异步请求是指网络请求发出之后,不必等待请求结果的到来,就可以去做其他的事情,当请求结果到来时,在做处理结果的动作。异步请求使用的是enqueue方法,如下所示:

private val client = OkHttpClient()

// GET请求
fun run() {
  val request = Request.Builder()
  .url("http://publicobject.com/helloworld.txt")
  .build()

  client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
      e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
      response.use {
        if (!response.isSuccessful) throw IOException("Unexpected code $response")

        for ((name, value) in response.headers) {
          println("$name: $value")
        }

        println(response.body!!.string())
      }
    }
  })
}

// POST请求
fun run() {
  val formBody = FormBody.Builder()
  .add("search", "Jurassic Park")
  .build()
  val request = Request.Builder()
  .url("https://en.wikipedia.org/w/index.php")
  .post(formBody)
  .build()

  client.newCall(request).execute().use { response ->
      if (!response.isSuccessful) throw IOException("Unexpected code $response")
           println(response.body!!.string())
  }
}

以下是源码:

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

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

class RealCall( // RealCall为真正的请求执行者
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
  
  override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }
  
}

class Dispatcher constructor() {
  private val readyAsyncCalls = ArrayDeque<AsyncCall>() // 正在准备中的异步请求队列
  private val runningAsyncCalls = ArrayDeque<AsyncCall>() // 运行中的异步请求
  private val runningSyncCalls = ArrayDeque<RealCall>()  // 同步请求
  
   private var executorServiceOrNull: ExecutorService? = null // 线程池

  @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!!
    }
  
  constructor(executorService: ExecutorService) : this() {
    this.executorServiceOrNull = executorService
  }
  
  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()
  }
  
  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
  }
}

异步请求是调度器dispatcher执行—client.dispatcher.enqueue(AsyncCall(responseCallback)),并通过回调(Callback)获取服务器返回的结果。

Dispatchercall加入到队列中,然后通过线程池来执行callDispatcher是一个任务调度器,它内部维护了三个双端队列:

  • readyAsyncCalls:准备运行的异步请求;
  • runningAsyncCalls:正在运行的异步请求;
  • runningSyncCalls:正在运行的同步请求;

新来请求放在队尾,执行请求从对头部取。

OKHttp双任务队列机制:

  • OkHttp实现异步请求采用了双任务队列机制,通过Dispatcher来调度任务
  • 新加入的异步任务AsyncCall进入等待队列readyAsyncCalls
  • 遍历readyAsyncCalls判断当前情况:是否超过最大并发数?是否超过同个主机最大请求数?
  • 满足条件直接把AsyncCall加入到正在执行的队列RunningAsyncCalls,并且使用线程池执行新加入的异步任务AsyncCall
  • AsyncCall执行结束,再次回到DispatcherpromoteAndExecute()

OKHttp的双任务队列

3 责任链模式与拦截器

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链,请求在这条链上传递,直到链上的某个对象处理此请求,或者每个对象都可以处理请求,并传给“下家”,直到最终链上每个对象都处理完。

这种模式可以避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求。

OkHttp拦截器就是基于责任链模式来实现的, 在请求到达时,拦截器会做一些处理(比如添加参数),然后传递给下一个拦截器处理:

责任链模式

4 OkHttp的拦截器

OkHttp提供了一系列的拦截器来处理相应的业务。也可以通过自定义拦截器,来实现自己的拦截业务。下面是一些常用的拦截器:

  • retryAndFollowUpInterceptor:失败和重定向拦截器。当请求内部抛出异常时,判定是否需要重试,当响应结果是3xx重定向时,构建新的请求并发送请求;
  • BridgeInterceptor:封装requestresponse拦截,负责把用户构造的请求转换为发送到服务器的请求,把服务器返回的响应转换为用户友好的响应;
  • CacheInterceptor:如果在OkHttpClient中配置了缓存,则将这个Resposne缓存起来;
  • ConnectInterceptor:连接服务,负责和服务器建立连接,这负责Dns解析和Socket连接;
  • CallServerInterceptor:传输http的头部和body数据;

OKHttp中完整的拦截链如下所示,在真正发起请求之前(CallServerInterceptor),将经过各个拦截器处理业务:

拦截器

4.1 CacheInterceptor网络请求缓存处理

OkHttp的缓存原则是,缓存拦截器会根据请求的信息和缓存的响应的信息来判断是否存在缓存可用,如果有可以使用的缓存,那么就返回该缓存给用户,否则就继续使用责任链模式来从服务器中获取响应。 当获取到响应的时候,又会把响应缓存到磁盘上面。涉及到的代码有:

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()) // 根据request的到cache中缓存的response

    val now = System.currentTimeMillis()

    // request判断缓存的策略,是否使用了网络,缓存或两者都使用
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

    cache?.trackResponse(strategy)
    val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE

    if (cacheCandidate != null && cacheResponse == null) {
      // The cache candidate wasn't applicable. Close it.
      cacheCandidate.body?.closeQuietly()
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(HTTP_GATEWAY_TIMEOUT)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build().also {
            listener.satisfactionFailure(call, it)
          }
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }

    if (cacheResponse != null) {
      listener.cacheConditionalHit(call, cacheResponse)
    } else if (cache != null) {
      listener.cacheMiss(call)
    }

    var networkResponse: Response? = null
    try {
      networkResponse = chain.proceed(networkRequest) // 调用下一个拦截器,决定从网络上来的到response
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    // 如果本地已经存在cacheResponse,那么让它和网络的到的networkResponse做比较,决定是否来更新缓存的cacheResponse
    if (cacheResponse != null) {
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        val response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers, networkResponse.headers))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis)
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build()

        networkResponse.body!!.close()

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache!!.trackConditionalCacheHit()
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()

    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        val cacheRequest = cache.put(response) // 缓存未经缓存过的response
        return cacheWritingResponse(cacheRequest, response).also {
          if (cacheResponse != null) {
            // This will log a conditional cache miss only.
            listener.cacheMiss(call)
          }
        }
      }

      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }

    return response
  }
}

4.2 addInterceptoraddNetworkdInterceptor区别

下面是构造拦截器链的过程,可以看到,通过addInterceptor()方法添加的拦截器是放在最前面的,而通过addNetworkInterceptor()方法添加的网络拦截器,则是在非WebSocket请求时,添加在ConnectInterceptorCallServerInterceptor之间的:

class RealCall( // RealCall为真正的请求执行者
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
  internal val eventListener: EventListener = client.eventListenerFactory.create(this)
  
   @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors // 在配置OkHttpClient时设置的interceptors
    interceptors += RetryAndFollowUpInterceptor(client) // 负责失败重试以及重定向
    interceptors += BridgeInterceptor(client.cookieJar) // 请求时,对必要的Header进行一些添加,接收响应时,移除非必要的Header
    interceptors += CacheInterceptor(client.cache) // 负责读取缓存直接返回、更新缓存
    interceptors += ConnectInterceptor // 负责和服务器建立连接
    if (!forWebSocket) {
      interceptors += client.networkInterceptors // 配置OkHttpClient时设置的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)
      }
    }
  }
}

addInterceptor(应用拦截器):

  • 不需要担心中间过程的响应,如重定向和重试;
  • 总是只调用一次,即使HTTP响应是从缓存中获取;
  • 观察应用程序的初衷,不关心OkHttp注入的头信息,如:If-None-Match
  • 允许短路而不调用Chain.proceed(),即中止调用;
  • 允许重试,使Chain.proceed()调用多次;

addNetworkInterceptor(网络拦截器):

  • 能够操作中间过程的响应,如重定向和重试;
  • 当网络短路而返回缓存响应时不被调用;
  • 只观察在网络上传输的数据;
  • 携带请求来访问连接;

5 连接池

5.1 TCP三次握手和四次挥手

  • 三次握手:客户端发送请求建立连接,服务端收到请求后立即回应,客户端收到回应后打开连接并通知服务端,服务端再次收到消息也打开连接;
  • 四次挥手:客户端数据发送完成告知服务端申请断开,服务端收到断并回应,客户端继续等待最后数据的传送,服务端业务完成再次发送回应消息并断开连接,客户端收到回应,再次发送一次确认,并断开;

三次握手与四次挥手

5.2 Socket连接池复用

连接池是为了解决频繁的进行建立Sokcet连接(TCP三次握手)和断开SocketTCP四次分手)。每次建立连接关闭连接都要三次握手、四次挥手,显然会造成效率低下,Http协议中有一种叫做KeepAlive机制,它可以在传输数据后仍然保持连接状态,当客户端需要再次传输数据时,直接使用空闲下来的连接而不需要重新建立连接。

OkHttp默认支持5个并发KeepAlive,链路默认的存活时间为5分钟:

Socket连接池复用

参考

https://blog.csdn.net/OneDeveloper/article/details/88381817

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:51:12  更:2022-04-07 22:53:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:31:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码