OKHttp3主流程再分析

发布时间 2023-08-24 14:29:52作者: 飘杨......

一、概述

  为什么要是用OKHttp3
  总结下来就两个大的方面
  一、成熟稳定
    OkHttp距今已有10多年的历史,在Android中大量且广泛的应用,在大、中、小项目中无处不在。可以这样说,只要是一个Android项目,网络框架的底层必定是OKHttp
  二、高效
    1.OkHttp的底层使用socket做数据的收发,对于同一台主机的多个请求可以公用一个socket连接,而不是没发送一次Http请求就关闭连接。连接复用后就可以极大的提高Http请求及响应的速度。因为请求的时候需要经历三次捂手,关闭的时候需要经过四次挥      手。一旦复用,这两步骤都省略,效率核新能就提上去了。
    2.连接池,新开链接是比较消耗时间和资源的,如果新开了一个链接使用后放入连接池,下次有相同类型的连接到来时直接从连接池拿而不是重新创建一个,这样可以极大的提升请求及响应的时间。
    3.OKHttp支持GZIP透明压缩,这样可以减少响应数据的大小,网络请求响应的速度
    4.OkHttp还支持Http/2,用于进一步提高响应速度,由于HTTP2支持头部压缩,相同类型的请求没请求一次相同的头部信息就会可以少发送一部分。相比一Http1.1其可以进行多路复用,对请求进行交错发送,进一步提升请求及响应的速度
    5.天然支持TLS,可以很方便的设置证书
    6.可以自定义拦截器,用于拦截请求及响应内容
    7.底层使用了okio,快速、稳定、内存消耗小,可以提升io的性能,从而提升okhttp的整体性能
      okio采用了segment机制进行了内存共享,极大减少了copy操作带来的内存消耗,加快了速写速度。
      okio引入了ByteString,使Byte[]与String之间的转换速度非常快
      okio的segment进行内存复用,上传大文件时完全不用担心OOM
      8.可以对DNS进行自定义解析

二、快速入门

  第一步:初始化OKHttp

okHttp = OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)//链接超时为2秒,单位为秒
            .writeTimeout(30, TimeUnit.SECONDS)//写入超时
            .readTimeout(20, TimeUnit.SECONDS)//读取超时
            .build()

  第二步:创建一个请求并塞入请求体

var json = "application/json; charset=utf-8".toMediaTypeOrNull();
        val requestBody = RequestBody.create(json,Gson().toJson(PostBean(Account("13386050182","123456"))))
        val request = Request.Builder()
            .url(BASE_URL + "tony/accounts/login")
            .addHeader("Content-Type", "application/json")
            .post(requestBody)
            .build()

  第三步:创建一个call

val call = okHttp.newCall(request)

  第四步:使用这个call发起请求

call.enqueue(object : Callback {
            //异步执行方法获取回调
            override fun onFailure(call: Call, e: IOException) {
                e.printStackTrace()
                KLog.e(e.message)
            }

            override fun onResponse(call: Call, response: Response) {
                KLog.e("获取响应结果:${response.body?.string()}")
            }

        })

 

三、主流程原理分析

  主流程调用流程图:

   


  1.OKHttp会创建一个用于发起请求的Call,并且可以发送同步或异步的请求(我们以异步请求举例)

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

  2.call调用enqueu发起异步请求,call只是一个接口,真正调用enqueue的是RealCall,即call的实现类

override fun enqueue(responseCallback: Callback) {
                    synchronized(this) {
                          check(!executed) { "Already Executed" }
                          executed = true
                        }
                    transmitter.callStart()
                    client.dispatcher.enqueue(AsyncCall(responseCallback))
                  }
                  可以从上面看到queue方法其实就是向dispatcher的enqueue方法中丢了一个Call对象。dispatcher的enqueue内部其实就是把Call扔进dispatcher的队列中。如下:
                  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.get().forWebSocket) {
                        val existingCall = findExistingCallWithHost(call.host())
                        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
                      }
                    }
                    promoteAndExecute()//把call从准备队列放入运行队列
                  }
                  下面看下promoteAndExecute()方法的内容
                  private fun promoteAndExecute(): Boolean {
                    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)//如果执行队列中有Call那就执行,RealCall的executeOn方法并把线程池传递进去
                    }

                    return isRunning
                  }
                  而executeOn方法也只是把当前要执行的代码加入线程池中而已,真正执行的其实是RealCall的run方法。如下:
                  fun executeOn(executorService: ExecutorService) {
                     ....省略
                      try {
                        executorService.execute(this)//把当前线程扔入线程池
                        success = true
                      } catch (e: RejectedExecutionException) {
                           ....省略
                        responseCallback.onFailure(this@RealCall, ioException)
                      } finally {
                        if (!success) {
                          client.dispatcher.finished(this) // This call is no longer running!
                        }
                      }
                    }
                    执行的这个run方法就非常重要了,几乎所有的重要方法都在这里面。如下:
                    override fun run() {
                      threadName("OkHttp ${redactedUrl()}") {
                        .....省略不重要代码
                        try {
                          //这句代码非常关键,请求在拦截器当中一级一级的传递,最终发起请求,然后获取请求结果再层层通过拦截器传递给客户端。返回一个response
                          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)
                        }
                      }
                    }
                  }

  3.getResponseWithInterceptorChan()里面主要是一层层的拦截器,如下:

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

                    var calledNoMoreExchanges = false
                    try {
                      val response = chain.proceed(originalRequest)//执行链式调用过程,最终返回response
                      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)
                      }
                    }
              }

  4.分析到这里其实主流程就结束了。再来回顾一下

    1.创建一个Call
    2.发起异步调用call.enqueue
    3.通过dispatcher.enqueue把call加入队列
    4.线程池执行RealCall的run方法
    5.执行拦截器链式调用发起请求及获取响应
    6.响应成功/失败调用回调函数
    7.完成