美文网首页
抽丝剥茧okhttp(四)OkHttpClient原理

抽丝剥茧okhttp(四)OkHttpClient原理

作者: 张哲1111 | 来源:发表于2018-03-30 17:17 被阅读72次

    接上篇抽丝剥茧okhttp(三) https://www.jianshu.com/p/cf59397dce1f
    下面是极简版的okhttp请求网络的流程图,之前分析过了Response、Request这两个涉及http本身的协议的封装。那么如何宏观上看整个请求流程呢?
    1 OkHttpClient负责把这些部件和配置装配起来
    2 用Call对象发出请求,
    3 在发送和接收过程中通过很多拦截器处理,
    4 之后我们接得了response对象,整个网络请求流程完成。
    一共四步,联想实际的网络请求不难理解。

    image.png

    那么这四步具体是如何实现的呢?我们一步一步的来抽丝剥茧。

    OkhttpClient

    OkhttpClient充当的是一个客户端的角色,负责收集请求,配置信息,创建用于实际发出请求的Call。下面根据我们实际项目顺序了解一下OkhttpClient的运行机制。

    1 .OkhttpClient的创建

    public OkHttpClient() {
        this(new Builder());
      }
    
      OkHttpClient(Builder builder) {
        this.dispatcher = builder.dispatcher;
        this.proxy = builder.proxy;
        this.protocols = builder.protocols;
        this.connectionSpecs = builder.connectionSpecs;
        this.interceptors = Util.immutableList(builder.interceptors);
        this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
        this.eventListenerFactory = builder.eventListenerFactory;
        this.proxySelector = builder.proxySelector;
        this.cookieJar = builder.cookieJar;
        this.cache = builder.cache;
        this.internalCache = builder.internalCache;
        this.socketFactory = builder.socketFactory;
    
        boolean isTLS = false;
        for (ConnectionSpec spec : connectionSpecs) {
          isTLS = isTLS || spec.isTls();
        }
    
        if (builder.sslSocketFactory != null || !isTLS) {
          this.sslSocketFactory = builder.sslSocketFactory;
          this.certificateChainCleaner = builder.certificateChainCleaner;
        } else {
          X509TrustManager trustManager = systemDefaultTrustManager();
          this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
          this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
        }
    
        this.hostnameVerifier = builder.hostnameVerifier;
        this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
            certificateChainCleaner);
        this.proxyAuthenticator = builder.proxyAuthenticator;
        this.authenticator = builder.authenticator;
        this.connectionPool = builder.connectionPool;
        this.dns = builder.dns;
        this.followSslRedirects = builder.followSslRedirects;
        this.followRedirects = builder.followRedirects;
        this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
        this.connectTimeout = builder.connectTimeout;
        this.readTimeout = builder.readTimeout;
        this.writeTimeout = builder.writeTimeout;
        this.pingInterval = builder.pingInterval;
    
        if (interceptors.contains(null)) {
          throw new IllegalStateException("Null interceptor: " + interceptors);
        }
        if (networkInterceptors.contains(null)) {
          throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
        }
      }
    

    OkhttpClient的创建也builder模式,但暴露给外部的是一new出来的,内部调用第二个采用builder构造方法。这样做是为了初始化builder中默认的重要参数,避免用户调用的时候这么多的参数用户很大可能上会配错一些东西。这样简化了操作。

    我们如何发起请求,获得response呢

     Request request = new Request.Builder()
          .url(url)
          .build();
    
      Response response = client.newCall(request).execute();
    

    OkhttpClient实现了Call.Factory的newCall(),这样为我们生产出来一个新的随时可以发起网络请求的Call对象;

    /**
       * Prepares the {@code request} to be executed at some point in the future.
       */
    @Override 
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
    

    可以看到他返回的是一个用RealCall从创建出来的而且是RealCall对象。

    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.eventListener = client.eventListenerFactory().create(call);
        return call;
      }
    

    这个RealCall对象正是对网络请求的真正发起者,起重大作用。RealCall实现了Call接口,我们看Call的定义。

    public interface Call extends Cloneable {
    //返回原始的Request
      Request request();
    
    //立即执行网络请求
      Response execute() throws IOException;
    
     //在将来的某一时刻发起请求
      void enqueue(Callback responseCallback);
    
    //判断是否被执行了
      boolean isExecuted();
    //判断是否被取消了
      boolean isCanceled();
    
    //创建出来一个新的Call对象
      Call clone();
    
      interface Factory {
        Call newCall(Request request);
      }
    }
    
    

    RealCall中复写的这些方法全权的执行了网络请求的工作。所以我们绕了这么大弯子终于看到在哪进行的网络请求了。

    
      @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    
    

    execute 和 enqueue中分别调用了
    client.dispatcher().finished(this);;和 client.dispatcher().enqueue(new AsyncCall(responseCallback));
    一个同步一个异步的。用dispatcher放到一个队列中进行分发运行,对于enqueue因为AsyncCall实现了Runnable接口,在dispatcher中的的线程池运行队列里的Runnable对象,执行executorService().execute(call);所以最后的网络请求还在这个AsyncCall对象对于runnable的实现中。他们共同的会调用

     Response response = getResponseWithInterceptorChain();
    

    获得了Response对象。那么getResponseWithInterceptorChain();又做了什么。

    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        interceptors.add(new CacheInterceptor(client.internalCache()));
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
          interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));
    
        Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());
    
        return chain.proceed(originalRequest);
      }
    

    这里添加了okhttp默认的几个拦截器,并进入chain.proceed(originalRequest);至此进入了一大堆拦截器各种拦截最后返回response的模式,关于拦截器我们以后再说,现在先过。经过层层拦截器我们终于拿到了最终的响应对象。如此推演我们可以推断出我们真正的请求应在最后一个拦截器中。我们看一下:就是那个CallServerInterceptor

    /** This is the last interceptor in the chain. It makes a network call to the server. */
    public final class CallServerInterceptor implements Interceptor {
      private final boolean forWebSocket;
    
      public CallServerInterceptor(boolean forWebSocket) {
        this.forWebSocket = forWebSocket;
      }
    
      @Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();
    
        long sentRequestMillis = System.currentTimeMillis();
    
        realChain.eventListener().requestHeadersStart(realChain.call());
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
    
        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
          // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
          // Continue" response before transmitting the request body. If we don't get that, return
          // what we did get (such as a 4xx response) without ever transmitting the request body.
          if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
            httpCodec.flushRequest();
            realChain.eventListener().responseHeadersStart(realChain.call());
            responseBuilder = httpCodec.readResponseHeaders(true);
          }
    
          if (responseBuilder == null) {
            // Write the request body if the "Expect: 100-continue" expectation was met.
            realChain.eventListener().requestBodyStart(realChain.call());
            long contentLength = request.body().contentLength();
            CountingSink requestBodyOut =
                new CountingSink(httpCodec.createRequestBody(request, contentLength));
            BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
    
            request.body().writeTo(bufferedRequestBody);
            bufferedRequestBody.close();
            realChain.eventListener()
                .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
          } else if (!connection.isMultiplexed()) {
            // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
            // from being reused. Otherwise we're still obligated to transmit the request body to
            // leave the connection in a consistent state.
            streamAllocation.noNewStreams();
          }
        }
    
        httpCodec.finishRequest();
    
        if (responseBuilder == null) {
          realChain.eventListener().responseHeadersStart(realChain.call());
          responseBuilder = httpCodec.readResponseHeaders(false);
        }
    
        Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
    
        int code = response.code();
        if (code == 100) {
          // server sent a 100-continue even though we did not request one.
          // try again to read the actual response
          responseBuilder = httpCodec.readResponseHeaders(false);
    
          response = responseBuilder
                  .request(request)
                  .handshake(streamAllocation.connection().handshake())
                  .sentRequestAtMillis(sentRequestMillis)
                  .receivedResponseAtMillis(System.currentTimeMillis())
                  .build();
    
          code = response.code();
        }
    
        realChain.eventListener()
                .responseHeadersEnd(realChain.call(), response);
    
        if (forWebSocket && code == 101) {
          // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
          response = response.newBuilder()
              .body(Util.EMPTY_RESPONSE)
              .build();
        } else {
          response = response.newBuilder()
              .body(httpCodec.openResponseBody(response))
              .build();
        }
    
        if ("close".equalsIgnoreCase(response.request().header("Connection"))
            || "close".equalsIgnoreCase(response.header("Connection"))) {
          streamAllocation.noNewStreams();
        }
    
        if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
          throw new ProtocolException(
              "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        }
    
        return response;
      }
    
      static final class CountingSink extends ForwardingSink {
        long successfulCount;
    
        CountingSink(Sink delegate) {
          super(delegate);
        }
    
        @Override public void write(Buffer source, long byteCount) throws IOException {
          super.write(source, byteCount);
          successfulCount += byteCount;
        }
      }
    }
    

    果不其然 我们看眼注释就知道了:This is the last interceptor in the chain. It makes a network call to the server. */
    也验证了我们之前的猜测。intercept方法中确确实实的执行了网络请求,但在okhttp3.internal包中的类涉及底层更多一些,本人目前精力有限,只能分析到这层,但目前看来对于轮子我们了解了差不多了,大件都拆了,小件拆了也不一定能用的到,所以就到这层。
    这里返回的 response就是我最终可以使用的response了。

    这个response我们一层一层返回上层 我们在哪能拿到还记得么?那就是execute()同步请求中直接返回,enqueue中的callback中

    @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    
    
      @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    
    
    public interface Callback {
      /**
       * Called when the request could not be executed due to cancellation, a connectivity problem or
       * timeout. Because networks can fail during an exchange, it is possible that the remote server
       * accepted the request before the failure.
       */
      void onFailure(Call call, IOException e);
    
      /**
       * Called when the HTTP response was successfully returned by the remote server. The callback may
       * proceed to read the response body with {@link Response#body}. The response is still live until
       * its response body is {@linkplain ResponseBody closed}. The recipient of the callback may
       * consume the response body on another thread.
       *
       * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
       * not necessarily indicate application-layer success: {@code response} may still indicate an
       * unhappy HTTP response code like 404 or 500.
       */
      void onResponse(Call call, Response response) throws IOException;
    }
    
    
    

    这样开发者朋友们就拿到可请求之后的Response了。这样我们终于看到okhttp官网上的那几段示例代码都做了什么事情了;我顺便贴出来,顺便也回味一下:

    Examples

    GET A URL

    This program downloads a URL and print its contents as a string. Full source.

    OkHttpClient client = new OkHttpClient();
    
    String run(String url) throws IOException {
      Request request = new Request.Builder()
          .url(url)
          .build();
    
      Response response = client.newCall(request).execute();
      return response.body().string();
    }
    

    POST TO A SERVER

    This program posts data to a service. Full source.

    public static final MediaType JSON
        = MediaType.parse("application/json; charset=utf-8");
    
    OkHttpClient client = new OkHttpClient();
    
    String post(String url, String json) throws IOException {
      RequestBody body = RequestBody.create(JSON, json);
      Request request = new Request.Builder()
          .url(url)
          .post(body)
          .build();
      Response response = client.newCall(request).execute();
      return response.body().string();
    }
    

    总结

    总结一下,整个网络请求过程中okhttpclient的角色就是准备材料,Call对象负责发起,但发起的意义在于把请求放到队列里执行,在此之后一直到最后请求成功这中间又精力了若干的拦截器。正是这些拦截器实现了更多更具有拓展性的工作,默认添加进去的拦截器如下:

     List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        interceptors.add(new CacheInterceptor(client.internalCache()));
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
          interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));
    

    从字面意义上来看包含了众多功能,而且我们后期还可以自己的定制自己的interceptor添加到这里面实现各式各样的功能需求,所以interceptor的设计师okhttp的绝妙之笔。关于interceptor我们后续有机会再行解析。

    相关文章

      网友评论

          本文标题:抽丝剥茧okhttp(四)OkHttpClient原理

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