美文网首页
okhttp——网络请求模型

okhttp——网络请求模型

作者: oceanLong | 来源:发表于2019-04-19 18:10 被阅读0次

    简介

    okhttp是Android中应用最广的http网络请求框架。结构优雅,性能强大。我们通过阅读它,对网络库的架构进行学习。本篇主要阅读okhttp的网络请求拦截链模型。

    基本结构

    okhttp采用拉截链的模型,将网络请求的各个部分,以一个个拦截器的方法,加入拦截链。


    拦截链

    详细代码

    我们知道,在okhttp的任务调度模型中,最终任务,会调用execute方法。我们先来看execute方法。

        override fun execute() {
          var signalledCallback = false
          transmitter.timeoutEnter()
          try {
            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)
          }
        }
    

    这个方法中,实现网络请求的关键调用是:getResponseWithInterceptorChain。其他主要还是调用回调或处理异常。

    拼装拦截链

      @Throws(IOException::class)
      fun getResponseWithInterceptorChain(): Response {
        // Build a full stack of interceptors.
        val interceptors = ArrayList<Interceptor>()
        interceptors.addAll(client.interceptors())
        interceptors.add(RetryAndFollowUpInterceptor(client))
        interceptors.add(BridgeInterceptor(client.cookieJar()))
        interceptors.add(CacheInterceptor(client.internalCache()))
        interceptors.add(ConnectInterceptor(client))
        if (!forWebSocket) {
          interceptors.addAll(client.networkInterceptors())
        }
        interceptors.add(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)
          if (transmitter.isCanceled) {
            closeQuietly(response)
            throw IOException("Canceled")
          }
          return response
        } catch (e: IOException) {
          calledNoMoreExchanges = true
          throw transmitter.noMoreExchanges(e) as Throwable
        } finally {
          if (!calledNoMoreExchanges) {
            transmitter.noMoreExchanges(null)
          }
        }
      }
    

    这块儿代码基本还是简单清晰的。先用ArrayList<Interceptor>保存拦截器的队列,然后生成RealInterceptorChain,最后调用proceed方法,获取response。

    我们先来看拦截器的抽象实现。

    /**
     * Observes, modifies, and potentially short-circuits requests going out and the corresponding
     * responses coming back in. Typically interceptors add, remove, or transform headers on the request
     * or response.
     */
    interface Interceptor {
      @Throws(IOException::class)
      fun intercept(chain: Chain): Response
    
      companion object {
        // This lambda conversion is for Kotlin callers expecting a Java SAM (single-abstract-method).
        @JvmName("-deprecated_Interceptor")
        inline operator fun invoke(
          crossinline block: (chain: Chain) -> Response
        ): Interceptor = object : Interceptor {
          override fun intercept(chain: Chain) = block(chain)
        }
      }
    
      interface Chain {
        fun request(): Request
    
        @Throws(IOException::class)
        fun proceed(request: Request): Response
    
        /**
         * Returns the connection the request will be executed on. This is only available in the chains
         * of network interceptors; for application interceptors this is always null.
         */
        fun connection(): Connection?
    
        fun call(): Call
    
        fun connectTimeoutMillis(): Int
    
        fun withConnectTimeout(timeout: Int, unit: TimeUnit): Chain
    
        fun readTimeoutMillis(): Int
    
        fun withReadTimeout(timeout: Int, unit: TimeUnit): Chain
    
        fun writeTimeoutMillis(): Int
    
        fun withWriteTimeout(timeout: Int, unit: TimeUnit): Chain
      }
    }
    

    intercept方法,就是拦截器的核心,输入Chain,返回Response。

    我们随意找一个Interceptor来进行阅读

    RetryAndFollowUpInterceptor

      @Override public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Transmitter transmitter = realChain.transmitter();
    
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
          transmitter.prepareToConnect(request);
    
          if (transmitter.isCanceled()) {
            throw new IOException("Canceled");
          }
    
          Response response;
          boolean success = false;
          try {
            response = realChain.proceed(request, transmitter, null);
            success = true;
          } catch (RouteException e) {
            // The attempt to connect via a route failed. The request will not have been sent.
            if (!recover(e.getLastConnectException(), transmitter, false, request)) {
              throw e.getFirstConnectException();
            }
            continue;
          }
      ...
    }
    
    

    这一段逻辑中,我们传入的是chain,即整个拦截链,会在中间调用realChain.proceed()。表示,在当前拦截器中,我们只做我们职责之类的逻辑,其余的逻辑,交给传入Chain的下一环

    由此我们得知,RealInterceptorChain其实是一次请求所要做的所有工作。每一个Interceptor只负责一部分工作。它们的顺序是从外到内,当完成自己部分的外层功能后,就会将接下来的工作,交给下一层去完成。RetryAndFollowUpInterceptor只负责重试和重定向这些外层工作,其实逻辑会交由拦截器链的下一环节实现。Interceptor本身不用关心下一级的Interceptor是谁。

    接下来,我们再看一下,RealInterceptorChain的逻辑。

    RealInterceptorChain

    RealInterceptorChain中有一个index的索引。它标识了当前拦截器链路进行到了哪一环。
    我们着重看RealInterceptorChain的proceed方法,看一下Interceptor是如何前进到下一环的。

    class RealInterceptorChain(
      private val interceptors: List<Interceptor>,
      private val transmitter: Transmitter,
      private val exchange: Exchange?,
      private val index: Int,
      private val request: Request,
      private val call: Call,
      private val connectTimeout: Int,
      private val readTimeout: Int,
      private val writeTimeout: Int
    ) : Interceptor.Chain {
    
      private var calls: Int = 0
    
        ......
      override fun proceed(request: Request): Response {
        return proceed(request, transmitter, exchange)
      }
    
      @Throws(IOException::class)
      fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
        if (index >= interceptors.size) throw AssertionError()
    
        calls++
    
        // If we already have a stream, confirm that the incoming request will use it.
        if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
          throw IllegalStateException("network interceptor " + interceptors[index - 1] +
              " must retain the same host and port")
        }
    
        // If we already have a stream, confirm that this is the only call to chain.proceed().
        if (this.exchange != null && calls > 1) {
          throw IllegalStateException("network interceptor " + interceptors[index - 1] +
              " must call proceed() exactly once")
        }
    
        // Call the next interceptor in the chain.
        val next = RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
        val interceptor = interceptors[index]
    
        @Suppress("USELESS_ELVIS")
        val response = interceptor.intercept(next) ?: throw NullPointerException(
            "interceptor $interceptor returned null")
    
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (exchange != null && index + 1 < interceptors.size && next.calls != 1) {
          throw IllegalStateException("network interceptor " + interceptor +
              " must call proceed() exactly once")
        }
    
        if (response.body() == null) {
          throw IllegalStateException(
              "interceptor $interceptor returned a response with no body")
        }
    
        return response
      }
    }
    

    这一段代码首先对index进行了检查,然后对call,exchange中的种种参数进行了检查。最后调用了

        // Call the next interceptor in the chain.
        val next = RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
        val interceptor = interceptors[index]
    
        @Suppress("USELESS_ELVIS")
        val response = interceptor.intercept(next) ?: throw NullPointerException(
            "interceptor $interceptor returned null")
    

    调用当前interceptor的intercept方法,并将下一个interceptor传入。

    小结

    okhttp的网络请求,采用了interceptor这样的结构,因为网络请求是一个层级深,分支少的结构。每一个层级并不关心下一个层级的实现。因此,这样的结构很合适。

    发散一下,对于层级深,分支少,交付结果一致的业务模型,我们也可以采用这种interceptor的模型。方便层级之前解耦合。

    如有问题,欢迎指正。

    相关文章

      网友评论

          本文标题:okhttp——网络请求模型

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