OkHttp

作者: 神的漾 | 来源:发表于2020-12-22 20:22 被阅读0次

    本文基于okhttp3.9x

    使用方式

    OkHttpClient client = new OkHttpClient(); // 创建okhttp对象
    
    String run(String url) throws IOException {
      Request request = new Request.Builder()
          .url(url)
          .build();
    
      try (Response response = client.newCall(request).execute()) {
        return response.body().string();
      }
    }
    

    可以看出这块用到了建造者模式.第一步创建OKHttpClient对象,然后创建Request,最后发起请求并获取请求结果Response.

    类结构

    public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    ...
    }
    
    • Cloneable 表示可以克隆的
    • Call.Factory 接口类,创建一个新的请求包含,newCall方法
    • WebSocket.Factory 接口类,创建一个新的web sockect,包含newWebSocket方法

    构造方法

      final Dispatcher dispatcher;
      final @Nullable Proxy proxy;
      final List<Protocol> protocols;
      final List<ConnectionSpec> connectionSpecs;
      final List<Interceptor> interceptors;
      final List<Interceptor> networkInterceptors;
      final EventListener.Factory eventListenerFactory;
      final ProxySelector proxySelector;
      final CookieJar cookieJar;
      final @Nullable Cache cache;
      final @Nullable InternalCache internalCache;
      final SocketFactory socketFactory;
      final @Nullable SSLSocketFactory sslSocketFactory;
      final @Nullable CertificateChainCleaner certificateChainCleaner;
      final HostnameVerifier hostnameVerifier;
      final CertificatePinner certificatePinner;
      final Authenticator proxyAuthenticator;
      final Authenticator authenticator;
      final ConnectionPool connectionPool;
      final Dns dns;
      final boolean followSslRedirects;
      final boolean followRedirects;
      final boolean retryOnConnectionFailure;
      final int connectTimeout;
      final int readTimeout;
      final int writeTimeout;
      final int pingInterval;
    
      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);
        }
      }
    

    可以看到有两个构造方法,在构造方法中会初始化一个Builder对象(建造者模式),在Builder中设置了OkHttp的属性,比如超时设置、拦截器等。
    接下来是Request对象

    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tag = builder.tag != null ? builder.tag : this;
      }
    

    可以看到Request对象也是通过建造者模式创建的,在这里配置了请求url,请求头等信息。

    /**
       * 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 */);
      }
    
    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
      }
    
      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;
      }
    

    然后调用了newCall方法创建了RealCall实例,然后通过RealCall发起请求,RealCall.newRealCall,RealCall是Call接口的实现类.异步请求调用了RealCall的enqueue()方法.

    @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));
      }
    

    这里实际上调用了Dispatcher的enqueue方法

    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

    这里的enqueue方法是个同步方法.首先判断队列是否已满小于maxRequest=64,并且相同host的请求小于maxRequestsPerHost=5,队列不满则开始在线程池中执行请求AsyncCall,AsyncCall继承了NamedRunnable抽象类,而NamedRunnable继承了Runnable接口,在run方法中调用了excute()方法.

    /**NamedRunnable中的方法**/
    @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              signalledCallback = true;
              responseCallback.onResponse(RealCall.this, response);
            }
          } catch (IOException e) {
            if (signalledCallback) {
              // Do not signal the callback twice!
              Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
            } else {
              eventListener.callFailed(RealCall.this, e);
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            client.dispatcher().finished(this);
          }
        }
    

    这里就开始了OkHttp核心的请求部分,在OkHttp中使用了责任链模式处理这一部分请求. 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拦截器

    上面代码中OKHttp通过各种拦截器处理请求,这里简单介绍下拦截器:

    • 自定义拦截器:提供给用户的定制的拦截器。
    • 重试拦截器(RetryAndFollowUpInterceptor):请求在失败的时候重新开始的拦截器。
    • 桥接拦截器(BridgeInterceptor):主要用来构造请求。
    • 缓存拦截器(CacheInterceptor):主要处理HTTP缓存。
    • 连接拦截器(ConnectInterceptor):主要处理HTTP链接。
    • 网络请求拦截器(CallServerInterceptor):负责发起网络请求。
      上面代码通过RealInterceptorChain的proceed(request)方法开始执行拦截器.
     public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
          RealConnection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
    
        calls++;
        //...
        // Call the next interceptor in the chain.
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
            connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
            writeTimeout);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
        // ...
        return response;
      }
    

    重试拦截器—RetryAndFollowUpInterceptor

    public Response intercept(Chain chain) throws IOException {
        //......
        int followUpCount = 0;
        Response priorResponse = null;
        //通过一个循环来重新尝试请求
        while (true) {
          if (canceled) {
            streamAllocation.release();
            throw new IOException("Canceled");
          }
          Response response;
          boolean releaseConnection = true;
          try {
            //1.调用下一个拦截器
            response = realChain.proceed(request, streamAllocation, null, null);
            releaseConnection = false;
          } catch (RouteException e) {
            //......
          } catch (IOException e) {
            //......
          }
          //......
          //2.检测response是否合法
          Request followUp = followUpRequest(response);
          if (followUp == null) {
            if (!forWebSocket) {
              streamAllocation.release();
            }
            //3.返回response,请求完成
            return response;
          }
          //最多尝试20次
          if (++followUpCount > MAX_FOLLOW_UPS) {
            streamAllocation.release();
            throw new ProtocolException("Too many follow-up requests: " + followUpCount);
          }
          //4.重新设置请求
          request = followUp;
          priorResponse = response;
        }
      }
    

    在这里面我们可以看到请求的重度是一个无限循环来处理的,同时在代码里还限制了请求的次数,最多尝试20次.里面的具体逻辑是:
    1.开启循环,继续调用下一个拦截器直到返回结果.
    2.通过followUpRequest方法检查response是否合法,检查逻辑是根据HTTP返回码检测(比如402这种),如果followup为null则返回结果,否则进行下一步.
    3.重新设置request,设置response(用于接下来重新构造response),执行第一步.

    桥接拦截器-BridgeInterceptor

    public Response intercept(Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();
        RequestBody body = userRequest.body();
        if (body != null) {
          MediaType contentType = body.contentType();
          if (contentType != null) {
            requestBuilder.header("Content-Type", contentType.toString());
          }
          long contentLength = body.contentLength();
          if (contentLength != -1) {
            requestBuilder.header("Content-Length", Long.toString(contentLength));
            requestBuilder.removeHeader("Transfer-Encoding");
          } else {
            requestBuilder.header("Transfer-Encoding", "chunked");
            requestBuilder.removeHeader("Content-Length");
          }
        }
    
        if (userRequest.header("Host") == null) {
          requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        }
    
        if (userRequest.header("Connection") == null) {
          requestBuilder.header("Connection", "Keep-Alive");
        }
        boolean transparentGzip = false;
        if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
          transparentGzip = true;
          requestBuilder.header("Accept-Encoding", "gzip");
        }
        List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
        if (!cookies.isEmpty()) {
          requestBuilder.header("Cookie", cookieHeader(cookies));
        }
    
        if (userRequest.header("User-Agent") == null) {
          requestBuilder.header("User-Agent", Version.userAgent());
        }
        Response networkResponse = chain.proceed(requestBuilder.build());
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
        Response.Builder responseBuilder = networkResponse.newBuilder()
            .request(userRequest);
        if (transparentGzip
            && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
            && HttpHeaders.hasBody(networkResponse)) {
          GzipSource responseBody = new GzipSource(networkResponse.body().source());
          Headers strippedHeaders = networkResponse.headers().newBuilder()
              .removeAll("Content-Encoding")
              .removeAll("Content-Length")
              .build();
          responseBuilder.headers(strippedHeaders);
          String contentType = networkResponse.header("Content-Type");
          responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
        }
        return responseBuilder.build();
      }
    

    这个的作用是设置请求头的各种参数,比如Content-Type,Connection,User-Agent等.

    缓存拦截器-CacheInterceptor

    public Response intercept(Chain chain) throws IOException {
        Response cacheCandidate = cache != null? cache.get(chain.request()): null;//1.取缓存
        long now = System.currentTimeMillis();
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //2.验证缓存
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse; //获取缓存
    
        if (cache != null) {
          cache.trackResponse(strategy);
        }
        // If we're forbidden from using the network and the cache is insufficient, fail.
        //如果我们被禁止使用网络并且缓存不足,失败
        if (networkRequest == null && cacheResponse == null) {
          return new Response.Builder()
              .request(chain.request())
              .protocol(Protocol.HTTP_1_1)
              .code(504)
              .message("Unsatisfiable Request (only-if-cached)")
              .body(Util.EMPTY_RESPONSE)
              .sentRequestAtMillis(-1L)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();
        }
    
        // If we don't need the network, we're done.
        //3.直接返回缓存
        if (networkRequest == null) {
          return cacheResponse.newBuilder()
              .cacheResponse(stripBody(cacheResponse))
              .build();
        }
        Response networkResponse = null;
        try {
          //4.没有缓存,执行下一个拦截器
          networkResponse = chain.proceed(networkRequest);
        } 
    
        // If we have a cache response too, then we're doing a conditional get.
        if (cacheResponse != null) {
          if (networkResponse.code() == HTTP_NOT_MODIFIED) {
            Response response = cacheResponse.newBuilder()
                .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();
            networkResponse.body().close();
    
            // Update the cache after combining headers but before stripping the
            // Content-Encoding header (as performed by initContentStream()).
            cache.trackConditionalCacheHit();
            //5.更新缓存
            cache.update(cacheResponse, response);
            return response;
          } else {
            closeQuietly(cacheResponse.body());
          }
        }
        //......
        if (cache != null) {
          if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
            // Offer this request to the cache.
            //6.保存缓存
            CacheRequest cacheRequest = cache.put(response);
            return cacheWritingResponse(cacheRequest, response);
          }
        }
        return response;
      }
    

    缓存拦截器流程如下:
    1.根据请求(以Request为键值)取出缓存;
    2.验证缓存是否可用,如果可用则直接取出缓存,否则下一步;
    3.继续执行下一个拦截器,直拉返回结果;
    4.如果之前有缓存,则更新缓存,否则新增缓存.
    缓存拦截器主要的工作就是处理缓存,知道了大致流程后我们接下来分析一下OKhttp是如何管理缓存的.首先我们分析缓存如何获取,在代码中看到是通过cache.get()得到.代码如下:

      final InternalCache internalCache = new InternalCache() {
        @Override public Response get(Request request) throws IOException {
          return Cache.this.get(request);
        }
    
        @Override public CacheRequest put(Response response) throws IOException {
          return Cache.this.put(response);
        }
    
        @Override public void remove(Request request) throws IOException {
          Cache.this.remove(request);
        }
    
        @Override public void update(Response cached, Response network) {
          Cache.this.update(cached, network);
        }
    
        @Override public void trackConditionalCacheHit() {
          Cache.this.trackConditionalCacheHit();
        }
    
        @Override public void trackResponse(CacheStrategy cacheStrategy) {
          Cache.this.trackResponse(cacheStrategy);
        }
      };
    

    可以看到缓存是通过InternalCache管理的,而InternalCache是接口类,但是并没有使用他的实现类,直接使用接口,它是Cache的内部子类,InternalCache又调用了Cache的方法.我们这里分析一下get()方法

    @Nullable Response get(Request request) {
        String key = key(request.url());
        DiskLruCache.Snapshot snapshot;
        Entry entry;
        try {
          snapshot = cache.get(key);
          if (snapshot == null) {
            return null;
          }
        } catch (IOException e) {
          return null;
        }
        try {
          entry = new Entry(snapshot.getSource(ENTRY_METADATA));
        } catch (IOException e) {
          Util.closeQuietly(snapshot);
          return null;
        }
        Response response = entry.response(snapshot);
        //......
        return response;
      }
    

    key使用的Request的url进行md5->hex算法(16进制)->再转成utf-8.
    可以看到缓存是通过DiskLruCache管理的,接下来我们分析下OKHTTP如何验证缓存.
    在上面代码中缓存最终来自于CacheStrategy.看一下代码.

    public Factory(long nowMillis, Request request, Response cacheResponse) {
          //......
          if (cacheResponse != null) {
            this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
            this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
            Headers headers = cacheResponse.headers();
            for (int i = 0, size = headers.size(); i < size; i++) {
              String fieldName = headers.name(i);
              String value = headers.value(i);
              //这里获取了缓存的时间限制
              if ("Date".equalsIgnoreCase(fieldName)) {
                servedDate = HttpDate.parse(value);
                servedDateString = value;
              } else if ("Expires".equalsIgnoreCase(fieldName)) {
                expires = HttpDate.parse(value);
              } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
                lastModified = HttpDate.parse(value);
                lastModifiedString = value;
              } else if ("ETag".equalsIgnoreCase(fieldName)) {
                etag = value;
              } else if ("Age".equalsIgnoreCase(fieldName)) {
                ageSeconds = HttpHeaders.parseSeconds(value, -1);
              }
            }
          }
        }
    
    public CacheStrategy get() {
          CacheStrategy candidate = getCandidate();
          if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
            // We're forbidden from using the network and the cache is insufficient.
            return new CacheStrategy(null, null);
          }
    
          return candidate;
        }
    
    private CacheStrategy getCandidate() {
         //......
          long ageMillis = cacheResponseAge();
          long freshMillis = computeFreshnessLifetime(); //计算缓存是否过期
         //......
          return new CacheStrategy(conditionalRequest, cacheResponse);
    }
    
    private long computeFreshnessLifetime() {
          //判断缓存是否过期
          CacheControl responseCaching = cacheResponse.cacheControl();
          if (responseCaching.maxAgeSeconds() != -1) {
            return SECONDS.toMillis(responseCaching.maxAgeSeconds());
          } else if (expires != null) {
            long servedMillis = servedDate != null
                ? servedDate.getTime()
                : receivedResponseMillis;
            long delta = expires.getTime() - servedMillis;
            return delta > 0 ? delta : 0;
          } else if (lastModified != null
              && cacheResponse.request().url().query() == null) {
            long servedMillis = servedDate != null
                ? servedDate.getTime()
                : sentRequestMillis;
            long delta = servedMillis - lastModified.getTime();
            return delta > 0 ? (delta / 10) : 0;
          }
          return 0;
        }
    

    我们知道在HTTP中判断缓存是否过期的依据就是根据请求头的字段来判断比如Last-Modified,expires(不同的HTTP版本有不同的字段表示).在OKHTTP中也是根据这些依据.
    在上面的代码中,我们可以看到首先会从缓存中获取请求头字段中的时间数据,然后在构造CacheStrategy判断缓存是否过期,最后决定缓存是否合法.

    ConnectInterceptor 连接拦截器

    ConnectInterceptor的作用就是建立一个与服务端的连接.

    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
    
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
      }
    

    可以看到连接来自于StreamAllocation的newStream方法.

    public HttpCodec newStream(
          OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
        int connectTimeout = chain.connectTimeoutMillis();
        int readTimeout = chain.readTimeoutMillis();
        int writeTimeout = chain.writeTimeoutMillis();
        boolean connectionRetryEnabled = client.retryOnConnectionFailure();
    
        try {
          RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
              writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
          HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
    
          synchronized (connectionPool) {
            codec = resultCodec;
            return resultCodec;
          }
        } catch (IOException e) {
          throw new RouteException(e);
        }
      }
    

    可以在newStream()方法中继续寻找连接.我们继续分析代码可以看到,OKhttp的连接是维护在一个连接池中的

    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
          boolean connectionRetryEnabled) throws IOException {
        boolean foundPooledConnection = false;
        RealConnection result = null;
        Route selectedRoute = null;
        Connection releasedConnection;
        Socket toClose;
        synchronized (connectionPool) {
          if (released) throw new IllegalStateException("released");
          if (codec != null) throw new IllegalStateException("codec != null");
          if (canceled) throw new IOException("Canceled");
    
          // Attempt to use an already-allocated connection. We need to be careful here because our
          // already-allocated connection may have been restricted from creating new streams.
          releasedConnection = this.connection;
          toClose = releaseIfNoNewStreams();
          if (this.connection != null) {
            // We had an already-allocated connection and it's good.
            result = this.connection;
            releasedConnection = null;
          }
          if (!reportedAcquired) {
            // If the connection was never reported acquired, don't report it as released!
            releasedConnection = null;
          }
    
          if (result == null) {
            // Attempt to get a connection from the pool.
            Internal.instance.get(connectionPool, address, this, null);
            if (connection != null) {
              foundPooledConnection = true;
              result = connection;
            } else {
              selectedRoute = route;
            }
          }
        }
        closeQuietly(toClose);
    
        if (releasedConnection != null) {
          eventListener.connectionReleased(call, releasedConnection);
        }
        if (foundPooledConnection) {
          eventListener.connectionAcquired(call, result);
        }
        if (result != null) {
          // If we found an already-allocated or pooled connection, we're done.
          return result;
        }
    
        // If we need a route selection, make one. This is a blocking operation.
        boolean newRouteSelection = false;
        if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
          newRouteSelection = true;
          routeSelection = routeSelector.next();
        }
    
        synchronized (connectionPool) {
          if (canceled) throw new IOException("Canceled");
    
          if (newRouteSelection) {
            // Now that we have a set of IP addresses, make another attempt at getting a connection from
            // the pool. This could match due to connection coalescing.
            List<Route> routes = routeSelection.getAll();
            for (int i = 0, size = routes.size(); i < size; i++) {
              Route route = routes.get(i);
              Internal.instance.get(connectionPool, address, this, route);
              if (connection != null) {
                foundPooledConnection = true;
                result = connection;
                this.route = route;
                break;
              }
            }
          }
    
          if (!foundPooledConnection) {
            if (selectedRoute == null) {
              selectedRoute = routeSelection.next();
            }
    
            // Create a connection and assign it to this allocation immediately. This makes it possible
            // for an asynchronous cancel() to interrupt the handshake we're about to do.
            route = selectedRoute;
            refusedStreamCount = 0;
            result = new RealConnection(connectionPool, selectedRoute);
            acquire(result, false);
          }
        }
    
        // If we found a pooled connection on the 2nd time around, we're done.
        if (foundPooledConnection) {
          eventListener.connectionAcquired(call, result);
          return result;
        }
    
        // Do TCP + TLS handshakes. This is a blocking operation.
        result.connect(
            connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());
    
        Socket socket = null;
        synchronized (connectionPool) {
          reportedAcquired = true;
    
          // Pool the connection.
          Internal.instance.put(connectionPool, result);
    
          // If another multiplexed connection to the same address was created concurrently, then
          // release this connection and acquire that one.
          if (result.isMultiplexed()) {
            socket = Internal.instance.deduplicate(connectionPool, address, this);
            result = connection;
          }
        }
        closeQuietly(socket);
    
        eventListener.connectionAcquired(call, result);
        return result;
      }
    

    以上是OKHttp获取连接的主要逻辑,方法比较复杂,我们这里总结一下.
    1.首先尝试从连接池中获取一个连接,获取连接的参数是地址.如果获取到连接,则返回,否则进行下一步;
    2.如果需要选择线程,则继续尝试获取连接.如果获取到连接,则返回,否则进行下一步;
    3.创建一个新的连接,然后建立与服务端的TCP连接.
    4.将连接加入线程池.

    CallServerInterceptor 服务端拦截器

    CallServerInterceptor是最后一个拦截器,理所当然这个拦截器负责向服务端发送数据.

    public Response intercept(Chain chain) throws IOException {
        //......
        //写入请求头数据
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
          //......
          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()) {
            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();
    
        realChain.eventListener()
            .responseHeadersEnd(realChain.call(), response);
    
        int code = response.code();
        if (forWebSocket && code == 101) {
          response = response.newBuilder()
              .body(Util.EMPTY_RESPONSE)
              .build();
        } else {
          //读取返回内容
          response = response.newBuilder()
              .body(httpCodec.openResponseBody(response))
              .build();
        }
        //......
        return response;
      }
    

    可以看到主要是由HttpCodec执行的数据写入以有读取.HttpCodec是一个接口,它实现有两个类,分别是Http1Codec(处理http1.,1请求)和http2Codec(处理Http2)请求.在HttpCodec的实现中主要通过okio与服务端通信 .在上一节的ConnectInterceptor我们知道,Okhttp与服务端建立了一个TCP连接,所以客户端的与服务端的通信是直接通过TCP协议层的,当数据返回时,OKhttp会将数据构造HTTP形式的数据.

    OkHttp的工作原理就分析到这里了。在上面的文章中,首先分析了OkHttp在发起请求的准备阶段工作,构造OkHttpClient以及Request,然后通过调度器Dispatcher处理请求任务(请求又分为同步请求和异步请求)。最后通过拦截器处理请求

    https://www.jianshu.com/p/7b29b89cd7b5
    https://www.jianshu.com/p/310ccf5cbea6
    https://blog.csdn.net/programerxiaoer/article/details/97837226

    相关文章

      网友评论

          本文标题:OkHttp

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