美文网首页
OkHttp原理解析之请求流程

OkHttp原理解析之请求流程

作者: 我看Android | 来源:发表于2021-08-03 17:15 被阅读0次

    本文基于OkHttp4.9.1

    关于OkHttp这个东西大家都很熟悉了吧,那么对于它的请求流程你了解吗,你还在停留在知其然而不知其所以然的阶段码。来看一下我对它的理解吧

    使用方式

        fun requestTest() {
            val url = "https://www.wanandroid.com/banner/json"
            val client = OkHttpClient();
            val request = Request.Builder()
                .url(url)
                .build()
           //异步请求
            client.newCall(request).enqueue(object : Callback {//1
                override fun onFailure(call: Call, e: IOException) {
    
                }
    
                override fun onResponse(call: Call, response: Response) {
    
                }
            })
            //同步请求
            client.newCall(request).execute()//2
        }
    

    以上代码是OkHttp的基础使用方式,也是我们最方便切入框架结构的代码块。

    异步请求

    源码阅读选择哪个作为入口呢,当然是最接近功能实现的enqueue()方法了了。进入enqueue()方法发现它是一个抽象方法,它所在的类也是一个接口类。那么只能寻求它的上级实现了,进入newCall()的逻辑看一下

      /** Prepares the [request] to be executed at some point in the future. */
      override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
    

    它是创建了一个RealCall对象。RealCall有三个参数 val client: OkHttpClient、 val originalRequest: Request、 val forWebSocket: Boolean。client负责OkHttp的通用配置,originalRequest负责OkHttp的请求发起,forWebSocket用来判断是否需要以WebSocket形式建立请求连接。既然找到了newCall()创建的对象,那么就可以看一下enqueue()方法的实现了

      override fun enqueue(responseCallback: Callback) {
        check(executed.compareAndSet(false, true)) { "Already Executed" }
    
        callStart()//3
        client.dispatcher.enqueue(AsyncCall(responseCallback))//4
      }
      private fun callStart() {
       //跟踪程序,在程序出错时进行错误分析
        this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
       //监听反馈
        eventListener.callStart(this)
      }
    

    在RealCall##enqueue()的注释3的主要就是负责程序的错误分析以及流程监听反馈,注释4是enqueue()方法的执行关键,它调用了dispatcher的enqueue()方法并且向其中传入了一个AsyncCall对象。dispatcher是一个Dispatcher对象,Dispatcher类在OkHttp中负责线程的调度处理,功能由ExecutorService实现,有兴趣的同学可以单独去看一下Dispatcher源码。现在看一下Dispatcher##enqueue()

      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 // 5
            if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue //6
    
            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
      }
    

    readyAsyncCalls是一个双向队列,负责保存即将运行的AsyncCall对象。promoteAndExecute()中会对readyAsyncCalls进行遍历,将满足条件的AsyncCall对象取出放入executableCalls集合中,executableCalls保存的就是正在执行的AsyncCall对象。条件筛选是注释5及注释6,目的是防止程序过载。筛选完成后会对重新赋值的executableCalls进行遍历,取出其中的AsyncCall对象执行executeOn()方法

        fun executeOn(executorService: ExecutorService) {
          client.dispatcher.assertThreadDoesntHoldLock()
    
          var success = false
          try {
            executorService.execute(this)
            success = true
          } catch (e: RejectedExecutionException) {
            val ioException = InterruptedIOException("executor rejected")
            ioException.initCause(e)
            noMoreExchanges(ioException)
            responseCallback.onFailure(this@RealCall, ioException)
          } finally {
            if (!success) {
              client.dispatcher.finished(this) // This call is no longer running!
            }
          }
        }
    

    AsyncCall##executeOn()核心只有一行executorService.execute(this),通过线程池来执行一个任务。AsyncCall自己实现了Runnable接口,所以只需要看一下AsyncCall##run()方法即可

        override fun run() {
          threadName("OkHttp ${redactedUrl()}") {
            var signalledCallback = false
            timeout.enter()
            try {
              val response = getResponseWithInterceptorChain()
              signalledCallback = true
              responseCallback.onResponse(this@RealCall, response)
            } 
            ......  
            ......      
          }
        }
    

    在AsyncCall##run()其实也只有一行关键代码 val response = getResponseWithInterceptorChain(),拿到请求返回,然后通过responseCallback将结果回传responseCallback就是我们注释1中传入的Callback。如此一个异步请求流程就完成了

    同步请求

    前文分析异步方法enqueue()实现在RealCall类中,那么同步方法execute()也实现在
    RealCall类中。

      override fun execute(): Response {
        check(executed.compareAndSet(false, true)) { "Already Executed" }
    
        timeout.enter()
        callStart()
        try {
          client.dispatcher.executed(this)
          return getResponseWithInterceptorChain()
        } finally {
          client.dispatcher.finished(this)
        }
      }
    

    直接就是返回了getResponseWithInterceptorChain()的返回结果
    至此OkHttp的同步异步调用流程便分析完了。关于getResponseWithInterceptorChain()方法我会在另一篇文章中解析

    如果你有什么疑问或者认为哪个地方解析有错误,欢迎您在评论区指正。技术都是交流出来的

    相关文章

      网友评论

          本文标题:OkHttp原理解析之请求流程

          本文链接:https://www.haomeiwen.com/subject/yndevltx.html