首页 > 其他分享 >OKHttp3主流程再分析

OKHttp3主流程再分析

时间:2023-08-24 14:33:45浏览次数:47  
标签:分析 请求 val interceptors OKHttp3 client call 主流程 response

一、概述

  为什么要是用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.完成

标签:分析,请求,val,interceptors,OKHttp3,client,call,主流程,response
From: https://www.cnblogs.com/tony-yang-flutter/p/17654040.html

相关文章

  • 【核污水排海影响】从大气压带-水陆循环角度分析对我国的影响
    目录:1、大气压带2、大自然水陆间循环3、总结。 一、大气压带大气压流向:一定是高气压向低气压流动。现在是夏季,大陆季风气候如下:夏季-世界高低压分布图夏季-大陆受到太平洋高压(夏威夷高气压带)、澳大利亚高压带影响,从东南向大陆低气压带流动。会成为东南季风。如图:夏季......
  • 三维模型OBJ格式轻量化压缩变形现象分析
    三维模型OBJ格式轻量化压缩变形现象分析  三维模型的OBJ格式轻量化压缩是一种常见的处理方法,它可以减小模型文件的体积,提高加载和渲染效率。然而,在进行轻量化压缩过程中,有时会出现模型变形的现象,即压缩后的模型与原始模型在外观上存在差异。本文将从数据丢失、算法优化和参......
  • 一个被低估的插件:IDEA+JProfiler=性能分析神器
    JProfiler17.1.3(IDEA插件)JProfiler9.2(可执行软件)IntelliJIDEA2017.2.5下载下载JProfiler(IDEA)插件方式1:在IDEA上直接下载Settings–plugins–Browserepositories 搜索JProfiler点击install按钮安装,然后从启IDEA工具 看到如下图片则说明安装完成 方式2:......
  • [20230809]ora-04030问题分析整理.txt
    [20230809]ora-04030问题分析整理.txt--//生产系统同事使用toad连接经常出现ora-04030错误。ORA-04030:outofprocessmemorywhentryingtoallocate123416bytes(QERHJhash-joi,kllcqas:kllsltba)--//仔细看joi确实不是join,开始猜测可能某个程序的sql语句选择hash-join,......
  • 【Ehcache技术专题】「入门到精通」带你一起从零基础进行分析和开发Ehcache框架的实战
    前言Ehcache是一个流行的Java缓存框架,它提供了一种快速、可扩展和高效的方式来缓存数据。它可以帮助企业应用程序提高性能并减少数据库负载,因为它可以缓存经常访问的数据。Ehcache的主要特点快速:Ehcache使用内存缓存数据,因此它可以快速地访问缓存数据,而不需要从磁盘或数据库中读取......
  • IIS日志分析
    https://learn.microsoft.com/zh-cn/archive/blogs/exchange_chs/log-parser-studioIIS日志——统计IP访问次数的一种方法使用LogParser对IIS服务器被Hit访问的IP进行次数统计,方便结合防火墙IP***列表对IIS网站进行日志审计报表的编写配置IIS网站的日志下载进行日志分析的两个工具L......
  • 生信:一起学生信分析 RNA-Seq上游 篇
    一起学生信分析RNA-Seq上游篇参考文章:https://zhuanlan.zhihu.com/p/345896914RNA-Seq分析介绍转录组是指某特定细胞类型产生的所有转录本的集合。转录组研究能够从整体水平研究基因功能以及基因结构,揭示特定生物学过程以及疾病发生过程中的分子机理,已广泛应用于基础研究......
  • 生信:一起学生信分析 RNA-Seq下游 篇
    一起学生信分析RNA-Seq下游篇DESeq2介绍专为高通量测序数据(特别是RNA-seq数据)设计,用于分析计数数据的差异表达,同样功能的还有limma和edgR。差异表达分析使用DESeq2进行差异分析本教程使用的数据下载链接:表达矩阵matrix_clean.txt下载:https://wwry.lanzouq.com/i6w......
  • 一个.net加密壳的挖矿木马分析
     样本md5:02B886B7B245F7CA52172F299D279A0F   问题:挖矿木马有时候可以启动,有时候起不来?WHY?逆向看看,结论:foreach(Processprocess3inProcess.GetProcesses()){if(process3.ProcessName.ToLower()=="taskmgr"||p......
  • 安防监控视频智能分析平台:安全帽/反光衣/安全带AI识别详解
    人工智能技术已经越来越多地融入到视频监控领域中,近期我们也发布了基于AI智能视频云存储/安防监控视频AI智能分析平台的众多新功能,该平台内置多种AI算法,可对实时视频中的人脸、人体、物体等进行检测、跟踪与抓拍,支持口罩佩戴检测、安全帽佩戴检测、人体检测、区域入侵检测及可拓展......