Okhttp和Retrofit原理分析

1 篇文章 0 订阅
1 篇文章 0 订阅

1.OkHttp源码

首先来一张okhttp源码的完整流程图

1.1.RealCall.getResponseWithInterceptorChain方法解析

首先看一个典型的同步请求过程

public String get(String url) throws IOException {
    //新建OKHttpClient客户端
    OkHttpClient client = new OkHttpClient();
    //新建一个Request对象
    Request request = new Request.Builder()
            .url(url)
            .build();
    //Response为OKHttp中的响应
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    }else{
        throw new IOException("Unexpected code " + response);
    }
}

/**
*  Prepares the {@code request} to be executed at some point in the future.
*  准备将要被执行的request
*/
@Override
public Call newCall(Request request) {
    return new RealCall(this, request);
}

上面的client.newCall(request)返回的是RealCall,RealCall才是真正的请求执行者,来看下RealCall的构造方法和execute方法。

protected RealCall(OkHttpClient client, Request originalRequest) {  
    this.client = client;    
    this.originalRequest = originalRequest;    
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
}

@Override
public Response execute() throws IOException {
    synchronized (this) {
        //检查这个 call是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一样的 call,可以利用 all#clone 方法进行克隆
        if (executed) throw new IllegalStateException("Already Executed"); 
        executed = true;
    }
    try {
        //不过度关注
        client.dispatcher.executed(this);
        //真正发出网络请求,解析返回结果的地方--需要关注的地方
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
    }finally {
        //不过度关注,返回执行状态
        client.dispatcher.finished(this);
    }
} 

真正发出网络请求,解析返回结果的,还是 getResponseWithInterceptorChain:

//拦截器的责任链。
private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //在配置 OkHttpClient 时设置的 interceptors;
    interceptors.addAll(client.interceptors());   
    //负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
    interceptors.add(retryAndFollowUpInterceptor); 
    //负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor
    //其实也就是给request和response添加header
    interceptors.add(new BridgeInterceptor(client.cookieJar()));    
    //负责读取缓存直接返回、更新或者写入缓存的 CacheInterceptor
    interceptors.add(new CacheInterceptor(client.internalCache()));   
    //负责和服务器建立连接的 ConnectInterceptor,比较复杂
    interceptors.add(new ConnectInterceptor(client));    
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
        //配置 OkHttpClient 时设置的 networkInterceptors
        interceptors.addAll(client.networkInterceptors());    
    }
    //负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor,也不深入研究和okio相关
    interceptors.add(new CallServerInterceptor(
            retryAndFollowUpInterceptor.isForWebSocket()));     

    Interceptor.Chain chain = new RealInterceptorChain(
            interceptors, null, null, null, 0, originalRequest);
    //开始链式调用        
    return chain.proceed(originalRequest); 
}

在chain.proceed(originalRequest);中开启链式调用

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    Connection connection) throws IOException {
  if (index >= interceptors.size()) throw new AssertionError();
  calls++;
  //***************略***************
  // Call the next interceptor in the chain.、
  //这里是责任链模式调用
  //实例化下一个拦截器对应的RealIterceptorChain对象
  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpCodec, connection, index + 1, request);
  //得到当前的拦截器
  Interceptor interceptor = interceptors.get(index);
  //调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对象传递下去
  Response response = interceptor.intercept(next);  
  //***************略***************
  return response;
}

看看interceptor.intercept(next)干了啥,又调用了chain.proceed地柜迭代啊
//核心代码,调用下一个拦截器
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

1.2自定义一个仿okhttp的不纯的责任链demo

上面拦截器责任链模式我们可以自己写个demo加深理解,就以我们平常的请假申请流程为例,员工提交申请(request)--主管审批--经理审批--总监审批到此流程完成给你resposne。

public interface ModelOkHttpInterceptor {
    //处理实际的任务
    public void intercept(Chain chain);

    interface Chain {
        //构建责任链
        void proceed(String user);
    }
}

public class RealChain implements ModelOkHttpInterceptor.Chain {
    private List<ModelOkHttpInterceptor> mInterceptors;
    private int mIndex;
    private String mUser;

    public RealChain(List<ModelOkHttpInterceptor> interceptors, int index, String user) {
        mInterceptors = interceptors;
        mIndex = index;
        mUser = user;
    }

    @Override
    public void proceed(String user) {
        mUser = user;
        if (mIndex > mInterceptors.size()) {
            System.out.print("错误,没有人能够审批了");
        }
        RealChain chain = new RealChain(mInterceptors, mIndex + 1, mUser);
        ModelOkHttpInterceptor interceptor = mInterceptors.get(mIndex);
        interceptor.intercept(chain);
    }

    public String getUser() {
        return mUser;
    }
}

public class StaffInterceptor implements ModelOkHttpInterceptor {
    @Override
    public void intercept(Chain chain) {
        String name = ((RealChain) chain).getUser();
        System.out.println(name + "提交加班申请");
        //员工提交申请,电子流责任链转到主管那里
        chain.proceed("主管");
    }
}

public class SupervisorInterceptor implements ModelOkHttpInterceptor {
    @Override
    public void intercept(Chain chain) {
        String name = ((RealChain) chain).getUser();
        System.out.println(name + "审批通过");
        //主管神品通过申请,电子流责任链转到部门经理那里
        chain.proceed("经理");
    }
}

public class ManagerInterceptor implements ModelOkHttpInterceptor {
    @Override
    public void intercept(Chain chain) {
        String name = ((RealChain) chain).getUser();
        System.out.println(name + "审批通过");
        //部门经理提交申请,电子流责任链转到总监那里
        chain.proceed("总监");
    }
}

public class ChiefInterceptor implements ModelOkHttpInterceptor {
    @Override
    public void intercept(Chain chain) {
        String name = ((RealChain) chain).getUser();
        //一般总监审批通过流程就结束了,这里就相当于request结束有response返回
        System.out.println(name + "审批通过");
    }
}

public class MainTest {

    public static void main(String[] args ){
        List<ModelOkHttpInterceptor> interceptorList = new ArrayList<>();
        interceptorList.add(new StaffInterceptor());
        interceptorList.add(new SupervisorInterceptor());
        interceptorList.add(new ManagerInterceptor());
        interceptorList.add(new ChiefInterceptor());
        //构建加班申请责任链
        ModelOkHttpInterceptor.Chain realChain = new RealChain(interceptorList, 0, "老三");
        //员工开始提交申请
        realChain.proceed("老三");
    }
}

2.几个主要的拦截器解析

2.1RetryAndFollowUpInterceptor:负责失败重试以及重定向拦截器

这个拦截器负责:当一个请求由于某种原因获取响应数据失败了,就回去尝试重连恢复,并且重新封装request(followUpRequest)最大重试次数20次。

/**
 * This interceptor recovers from failures and follows redirects as necessary. 
 * 负责失败重试以及重定向的
 * It may throw an {@link IOException} if the call was canceled.
 */
public final class RetryAndFollowUpInterceptor implements Interceptor {
  /**
   * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
   * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
   * 重试的最大尝试次数,超过将抛出异常
   */
  private static final int MAX_FOLLOW_UPS = 20;
  //OkHttpClient成员
  private final OkHttpClient client;
  //这个类暂时先放过
  private StreamAllocation streamAllocation;
  //这个也先放过
  private boolean forWebSocket;
  //请求被取消
  private volatile boolean canceled;

  public RetryAndFollowUpInterceptor(OkHttpClient client) {
    this.client = client;
  }

  /**
   * 同步的立即取消socket连接请求
   */
  public void cancel() {
    canceled = true;
    StreamAllocation streamAllocation = this.streamAllocation;
    if (streamAllocation != null) streamAllocation.cancel();
  }
   
  //*******************省略了一些set方法**********************

  /**
   * 主要爱分析这个方法
   * @param chain
   * @return
   * @throws IOException
   */
  @Override public Response intercept(Chain chain) throws IOException {
    //拿到拦截链中的请求
    Request request = chain.request();
    //连接池,获取一个正确的连接
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()));
    //重定向次数初始化为0
    int followUpCount = 0;
    Response priorResponse = null;
    //开始循环
    while (true) {
      //请求被取消直接抛异常
      if (canceled) {
        //连接被释放
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        //核心代码,调用下一个拦截器
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        // 连接路由尝试失败,请求将不会被发送
        if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
        releaseConnection = false;
        //重试啊
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        //和服务器交互失败,请求也不会被发送
        if (!recover(e, false, request)) throw e;
        releaseConnection = false;
        //重试啊
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        // 抛出未知异常,释放资源
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      //如果上一次重试获取的response存在,将其boby置为空(没看明白干嘛)
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                .body(null)
                .build())
            .build();
      }
      //新的重试request封装:添加请求头,添加连接超时
      Request followUp = followUpRequest(response);
      //followUp为空(看下面followUpRequest这个方法的注释如果follow-up不必要或者不可用就返回空)
      //重试流程结束,在这里跳出。
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());
      //超过最大重试次数,释放连接,抛出异常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      //异常处理
      if (followUp.body() instanceof UnrepeatableRequestBody) {
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(
            client.connectionPool(), createAddress(followUp.url()));
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }
      //request重新赋值
      request = followUp;
      priorResponse = response;
    }
  }

  //*******************省略一些方法********************** 
  /**
   * Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
   * either add authentication headers, follow redirects or handle a client request timeout. If a
   * follow-up is either unnecessary or not applicable, this returns null.
   * 对重试请求做了下封装:添加header,连接timeout
   */
  private Request followUpRequest(Response userResponse) throws IOException {
    if (userResponse == null) throw new IllegalStateException();
    Connection connection = streamAllocation.connection();
    Route route = connection != null
        ? connection.route()
        : null;
    int responseCode = userResponse.code();

    final String method = userResponse.request().method();
    switch (responseCode) {
      case HTTP_PROXY_AUTH:
        Proxy selectedProxy = route != null
            ? route.proxy()
            : client.proxy();
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
        }
        return client.proxyAuthenticator().authenticate(route, userResponse);

      case HTTP_UNAUTHORIZED:
        return client.authenticator().authenticate(route, userResponse);

      case HTTP_PERM_REDIRECT:
      case HTTP_TEMP_REDIRECT:
        // "If the 307 or 308 status code is received in response to a request other than GET
        // or HEAD, the user agent MUST NOT automatically redirect the request"
        if (!method.equals("GET") && !method.equals("HEAD")) {
          return null;
        }
        // fall-through
      case HTTP_MULT_CHOICE:
      case HTTP_MOVED_PERM:
      case HTTP_MOVED_TEMP:
      case HTTP_SEE_OTHER:
        // Does the client allow redirects?
        if (!client.followRedirects()) return null;

        String location = userResponse.header("Location");
        if (location == null) return null;
        HttpUrl url = userResponse.request().url().resolve(location);

        // Don't follow redirects to unsupported protocols.
        if (url == null) return null;

        // If configured, don't follow redirects between SSL and non-SSL.
        boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
        if (!sameScheme && !client.followSslRedirects()) return null;

        // Redirects don't include a request body.
        Request.Builder requestBuilder = userResponse.request().newBuilder();
        if (HttpMethod.permitsRequestBody(method)) {
          if (HttpMethod.redirectsToGet(method)) {
            requestBuilder.method("GET", null);
          } else {
            requestBuilder.method(method, null);
          }
          requestBuilder.removeHeader("Transfer-Encoding");
          requestBuilder.removeHeader("Content-Length");
          requestBuilder.removeHeader("Content-Type");
        }

        // When redirecting across hosts, drop all authentication headers. This
        // is potentially annoying to the application layer since they have no
        // way to retain them.
        if (!sameConnection(userResponse, url)) {
          requestBuilder.removeHeader("Authorization");
        }

        return requestBuilder.url(url).build();

      case HTTP_CLIENT_TIMEOUT:
        // 408's are rare in practice, but some servers like HAProxy use this response code. The
        // spec says that we may repeat the request without modifications. Modern browsers also
        // repeat the request (even non-idempotent ones.)
        if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
          return null;
        }

        return userResponse.request();

      default:
        return null;
    }
  }

  /**
   * Returns true if an HTTP request for {@code followUp} can reuse the connection used by this
   * engine.
   * 判断request请求是否可以复用
   */
  private boolean sameConnection(Response response, HttpUrl followUp) {
    HttpUrl url = response.request().url();
    return url.host().equals(followUp.host())
        && url.port() == followUp.port()
        && url.scheme().equals(followUp.scheme());
  }
}

2.2BridgeInterceptor:

BridgeInterceptor的注释意思是根据用户请求构建网络请求,使得它继续去访问网络,最后根据网络响应构建用户响应, 其实就是卫request添加请求头,为response添加响应头。

/**
 * Bridges from application code to network code. First it builds a network request from a user
 * request. Then it proceeds to call the network. Finally it builds a user response from the network
 * response.
 * 上面意思是根据用户请求构建网络请求,使得它继续去访问网络,最后根据网络响应构建用户响应。
 * 其实就是卫request添加请求头,为response添加响应头。
 */
public final class BridgeInterceptor implements Interceptor {
  private final CookieJar cookieJar;

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

  @Override 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();
      //添加contenttype
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        //如果body有值,请求头设置body的内容长度,并且移除传输编码
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        //否则,设置传输编码(这里是分块传输编码),移除内容长度值
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }
    //添加Host
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    //添加Connection
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == 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());
    }
    //开始给resposne添加header
    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);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
}

2.3ConnectInterceptor

ConnectInterceptor的intercept内部代码很少,关内容主要是咋在streamAllocation.newStream这里比较复杂。newStream内部findHealthyConnection是找到一个可用连接的意思重点就在这,通过找到的可用连接newCodec()返回了HttpCodec的实现类Http1Codec对象。

public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;

  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}

主要看看findHealthyConnection()的实现

public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
  int connectTimeout = client.connectTimeoutMillis();
  int readTimeout = client.readTimeoutMillis();
  int writeTimeout = client.writeTimeoutMillis();
  boolean connectionRetryEnabled = client.retryOnConnectionFailure();

  try {
     //findHealthyConnection这个方法是重点
    RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
        writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);

    HttpCodec resultCodec;
    if (resultConnection.http2Connection != null) {
      resultCodec = new Http2Codec(client, this, resultConnection.http2Connection);
    } else {
      resultConnection.socket().setSoTimeout(readTimeout);
      resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
      resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
      resultCodec = new Http1Codec(
          client, this, resultConnection.source, resultConnection.sink);
    }

    synchronized (connectionPool) {
      codec = resultCodec;
      return resultCodec;
    }
  } catch (IOException e) {
    throw new RouteException(e);
  }
}

流程:先检测链接是否可用:

a 可用的话直接返回,结束流程。

b 不可用,先去连接池中查找:

连接池找到:结束流程。

未找到:提供address,再次去连接池中查找。如果找到了直接结束流程,如果没找到:生成一个新的连接,并且将这个新的链接加入到连接池,然后返回这个新链接。

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
    int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
    boolean doExtensiveHealthChecks) throws IOException {
  while (true) {
    RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
        pingIntervalMillis, connectionRetryEnabled);

    // If this is a brand new connection, we can skip the extensive health checks.
    synchronized (connectionPool) {
      if (candidate.successCount == 0) {
        return candidate;
      }
    }
    // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
    // isn't, take it out of the pool and start again.
    if (!candidate.isHealthy(doExtensiveHealthChecks)) {//判断连接是否可用
      noNewStreams();//连接不可用,移除
      continue;//不可用,就一直持续
    }

    return candidate;
  }
}

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, 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) { //
    //经过releaseIfNoNewStreams,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;
    }
    
    //无可用连接,去连接池connectionPool中获取
    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) {//上面通过去连接池中找,如果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.
    //提供address,再次从连接池中获取连接
      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, pingIntervalMillis,
      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.
    //如果是一个http2连接,http2连接应具有多路复用特性,
    if (result.isMultiplexed()) {
      socket = Internal.instance.deduplicate(connectionPool, address, this);
      result = connection;
    }
  }
  closeQuietly(socket);

  eventListener.connectionAcquired(call, result);
  return result;
}

2.4CacheInterceptor

CacheInterceptor核心还是调用下一个拦截器,从网络获取响应数据,流程:

  • 1.首先根据requst来判断cache中是否存在缓存,如有存在取出这个response
  • 2.根据request获取缓存策略:网络,缓存,网络+缓存
  • 3.一些异常情况判断:

         (1)既无网络请求访问,也没有缓存,直接返回504

         (2)没有网络请求访问,但是有缓存直接返回缓存

  • 4.调用下一个拦截器,从网络获取响应数据
  • 5.如果本地存在cacheResponse,网络response和cacheResponse作比较,判断是否更新cacheResponse
  • 6.没有cacheResponse,直接写入网络response到缓存
public final class CacheInterceptor implements Interceptor {
  @Override public Response intercept(Chain chain) throws IOException {
    //获取到缓存----第一步
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
    //CacheStrategy缓存策略类,管理request和response的缓存----第二步
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }
    //存在缓存但是不适用,关闭
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    //如果禁止访问网络,并且无缓存,返回504----第三步
    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(EMPTY_BODY)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // If we don't need the network, we're done.
    //没有网络请求,直接返回缓存--还是第三步
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      //网络请求拦截器----第四步
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      //如果因为io或者其他原因崩溃,不泄露缓存并关闭
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    //如果已经存在缓存response,并且正在做条件get请求
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .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();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (HttpHeaders.hasBody(response)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      //写入缓存----第六步
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }
}

2.5CallServerInterceptor:发送和接收数据

3.请求过程

3.1同步请求流程

1.1有提到过,不说了

3.2异步请求流程

典型流程

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override 
      public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override 
      public void onResponse(Call call, Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        Headers responseHeaders = response.headers();
        for (int i = 0, size = responseHeaders.size(); i < size; i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });
  }

和同步不同的地方主要是enqueue,看这个方法:

//异步任务使用
@Override 
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    //调用了上面我们没有详细说的Dispatcher类中的enqueue(Call )方法.接着继续看--分析1
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); //正在准备中的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); //运行中的异步请求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); //同步请求

/**
**分析1
**/
synchronized void enqueue(AsyncCall call) { 
    //如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,同时利用线程池//执行call;否者将call加入到readyAsyncCalls中
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
        runningAsyncCalls.add(call);
        //call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类--分析2
        executorService().execute(call);
    } else {
        readyAsyncCalls.add(call);
    }
}

//异步请求
/**
**分析2
**/
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    private AsyncCall(Callback responseCallback) {
        super("OkHttp %s", redactedUrl());
        this.responseCallback = responseCallback;
    }

    String host() {
        return originalRequest.url().host();
    }

    Request request() {
        return originalRequest;
    }

    RealCall get() {
        return RealCall.this;
    }

    @Override protected void execute() {
        boolean signalledCallback = false;
        try {
            //破案了,异步请求也是调用getResponseWithInterceptorChain处理来获得response,
            //异步任务同样也是会经过一系列的拦截器
            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 {
                responseCallback.onFailure(RealCall.this, e);
            }
        } finally {
            client.dispatcher().finished(this);
        }
    }
}

4.自定义一个拦截器(LogInterceptor)

就以最常见的LoggingInterceptor为例子

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();
    
    //1.请求前--打印请求信息
    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    //2.网络请求
    Response response = chain.proceed(request);

    //3.网络响应后--打印响应信息
    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

自定义拦截的添加也分两种方式Application Intercetor 和 NetworkInterceptor,他们的区别如下图:

4.1添加 Application Interceptor,request是最初的request,response 就是最终的响应,得到日志相对较少

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();

响应数据:
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

4.2添加 NetworkInterceptor,可以得到的是更多的信息,重定向的信息也会得到。

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();


INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txt

INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值