Okhttp分析

https://futurestud.io/tutorials/retrofit-getting-started-and-android-client

class  Request
{
  private final HttpUrl url;
  private final String method;
  private final Headers headers;
  private final RequestBody body;
  private final Object tag;

  private volatile URI javaNetUri; // Lazily initialized.
  private volatile CacheControl cacheControl; // Lazily initialized.

  public CacheControl cacheControl() {
    CacheControl result = cacheControl;//这里Request中的cacheControl为null
    return result != null ? result : (cacheControl = CacheControl.parse(headers));//执行cacheControl = CacheControl.parse(headers)
  }
    
}

class OkHttpClient extends Call.Factory
{
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  proxySelector = ProxySelector.getDefault();
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;

  @Override 
  public Call newCall(Request request) {
    return new RealCall(this, request);
  }
}

class RealCall extends Call
{
  private final OkHttpClient client;

  // Guarded by this.
  private boolean executed;
  volatile boolean canceled;

  /** The application's original request unadulterated by redirects or auth headers. */
  Request originalRequest;
  HttpEngine engine;

    @Override 
    public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain(false);
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
          client.dispatcher().finished(this);
        }
    }

    private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
        Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
        return chain.proceed(originalRequest);
    }

    class ApplicationInterceptorChain
    {
        @Override 
        public Response proceed(Request request) throws IOException {
          // If there's another interceptor in the chain, call that.
          if (index < client.interceptors().size()) {
            Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
            Interceptor interceptor = client.interceptors().get(index);
            Response interceptedResponse = interceptor.intercept(chain);

            if (interceptedResponse == null) {
              throw new NullPointerException("application interceptor " + interceptor
                  + " returned null");
            }

            return interceptedResponse;
          }

          // No more interceptors. Do HTTP.
          return getResponse(request, forWebSocket);
        }
    }

    Response getResponse(Request request, boolean forWebSocket) throws IOException {
        // Copy body metadata to the appropriate request headers.
        RequestBody body = request.body();
        if (body != null) {
          Request.Builder requestBuilder = request.newBuilder();

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

          request = requestBuilder.build();
        }

        // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
        engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

        int followUpCount = 0;
        while (true) {
          if (canceled) {
            engine.releaseStreamAllocation();
            throw new IOException("Canceled");
          }

          boolean releaseConnection = true;
          try {
            //**这里开始发送请求**
            engine.sendRequest();
            engine.readResponse();
            releaseConnection = false;
          } catch (RequestException e) {
            // The attempt to interpret the request failed. Give up.
            throw e.getCause();
          } catch (RouteException e) {
            // The attempt to connect via a route failed. The request will not have been sent.
            HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
            if (retryEngine != null) {
              releaseConnection = false;
              engine = retryEngine;
              continue;
            }
            // Give up; recovery is not possible.
            throw e.getLastConnectException();
          } catch (IOException e) {
            // An attempt to communicate with a server failed. The request may have been sent.
            HttpEngine retryEngine = engine.recover(e, null);
            if (retryEngine != null) {
              releaseConnection = false;
              engine = retryEngine;
              continue;
            }

            // Give up; recovery is not possible.
            throw e;
          } finally {
            // We're throwing an unchecked exception. Release any resources.
            if (releaseConnection) {
              StreamAllocation streamAllocation = engine.close();
              streamAllocation.release();
            }
          }

          Response response = engine.getResponse();
          Request followUp = engine.followUpRequest();

          if (followUp == null) {
            if (!forWebSocket) {
              engine.releaseStreamAllocation();
            }
            return response;
          }

          StreamAllocation streamAllocation = engine.close();

          if (++followUpCount > MAX_FOLLOW_UPS) {
            streamAllocation.release();
            throw new ProtocolException("Too many follow-up requests: " + followUpCount);
          }

          if (!engine.sameConnection(followUp.url())) {
            streamAllocation.release();
            streamAllocation = null;
          }

          request = followUp;
          engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
              response);
        }
  }
}

class Dispatcher
{
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
}


class HttpEngine
{   
    public final StreamAllocation streamAllocation;

    public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody,
      boolean callerWritesRequestBody, boolean forWebSocket, StreamAllocation streamAllocation,
      RetryableSink requestBodyOut, Response priorResponse) {
        this.client = client;
        this.userRequest = request;
        this.bufferRequestBody = bufferRequestBody;
        this.callerWritesRequestBody = callerWritesRequestBody;
        this.forWebSocket = forWebSocket;
        this.streamAllocation = streamAllocation != null
            ? streamAllocation
            : new StreamAllocation(client.connectionPool(), createAddress(client, request));
        this.requestBodyOut = requestBodyOut;
        this.priorResponse = priorResponse;
  }

  public void sendRequest() throws RequestException, RouteException, IOException {
    if (cacheStrategy != null) return; // Already sent.
    if (httpStream != null) throw new IllegalStateException();

    Request request = networkRequest(userRequest);// 1为用户创建的Request添加一些必要的请求头

    InternalCache responseCache = Internal.instance.internalCache(client);//这里的Internal对象只在OkhttpClient中被实现,这里返回OkhttpClient中的internalCache为null
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;//responseCache为null,所以这里也为null

    long now = System.currentTimeMillis();//当前毫秒
    cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();//没啥操作
    networkRequest = cacheStrategy.networkRequest;//这里为上面的request
    cacheResponse = cacheStrategy.cacheResponse;//null

    if (responseCache != null) {//跳过
      responseCache.trackResponse(cacheStrategy);
    }

    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.
    if (networkRequest == null && cacheResponse == null) {//跳过
      userResponse = new Response.Builder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .build();
      return;
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {//跳过
      userResponse = cacheResponse.newBuilder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .cacheResponse(stripBody(cacheResponse))
          .build();
      userResponse = unzip(userResponse);
      return;
    }

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean success = false;
    try {
      httpStream = connect();
      httpStream.setHttpEngine(this);

      if (writeRequestHeadersEagerly()) {
        long contentLength = OkHeaders.contentLength(request);
        if (bufferRequestBody) {
          if (contentLength > Integer.MAX_VALUE) {
            throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
                + "setChunkedStreamingMode() for requests larger than 2 GiB.");
          }

          if (contentLength != -1) {
            // Buffer a request body of a known length.
            httpStream.writeRequestHeaders(networkRequest);
            requestBodyOut = new RetryableSink((int) contentLength);
          } else {
            // Buffer a request body of an unknown length. Don't write request headers until the
            // entire body is ready; otherwise we can't set the Content-Length header correctly.
            requestBodyOut = new RetryableSink();
          }
        } else {
          httpStream.writeRequestHeaders(networkRequest);
          requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
        }
      }
      success = true;
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (!success && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }
  }
  
  private HttpStream connect() throws RouteException, RequestException, IOException {
    boolean doExtensiveHealthChecks = !networkRequest.method().equals("GET");//请求是否为!Get方法
    return streamAllocation.newStream(client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis(),
        client.retryOnConnectionFailure(), doExtensiveHealthChecks);
  }


}

class CahceStrategy
{   
  /** The request to send on the network, or null if this call doesn't use the network. */
  public final Request networkRequest;

  /** The cached response to return or validate; or null if this call doesn't use a cache. */
  public final Response cacheResponse;

  private CacheStrategy(Request networkRequest, Response cacheResponse) {
    this.networkRequest = networkRequest;
    this.cacheResponse = cacheResponse;
  }

    class Factory
    {
        public Factory(long nowMillis, Request request, Response cacheResponse) {
          this.nowMillis = nowMillis;
          this.request = request;
          this.cacheResponse = cacheResponse;
          ...
        }
        
        public CacheStrategy get() {
          CacheStrategy candidate = getCandidate();

          if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {//onlyIfCached()为false,将不会执行里面
            // We're forbidden from using the network and the cache is insufficient.
            return new CacheStrategy(null, null);
          }

          return candidate;
       }
       private CacheStrategy getCandidate() {
          // No cached response.
          if (cacheResponse == null) {
            return new CacheStrategy(request, null);
          }
          ...
       }
    }
}

class CacheControl
{
    public static CacheControl parse(Headers headers) {
        boolean noCache = false;
        boolean noStore = false;
        int maxAgeSeconds = -1;
        int sMaxAgeSeconds = -1;
        boolean isPrivate = false;
        boolean isPublic = false;
        boolean mustRevalidate = false;
        int maxStaleSeconds = -1;
        int minFreshSeconds = -1;
        boolean onlyIfCached = false;
        boolean noTransform = false;

        boolean canUseHeaderValue = true;
        String headerValue = null;

        for (int i = 0, size = headers.size(); i < size; i++) {
          String name = headers.name(i);
          String value = headers.value(i);

          if (name.equalsIgnoreCase("Cache-Control")) {//heards中没有
            if (headerValue != null) {
              // Multiple cache-control headers means we can't use the raw value.
              canUseHeaderValue = false;
            } else {
              headerValue = value;
            }
          } else if (name.equalsIgnoreCase("Pragma")) {//heards中没有
            // Might specify additional cache-control params. We invalidate just in case.
            canUseHeaderValue = false;
          } else {
            continue;
          }

          int pos = 0;
          while (pos < value.length()) {
            int tokenStart = pos;
            pos = HeaderParser.skipUntil(value, pos, "=,;");
            String directive = value.substring(tokenStart, pos).trim();
            String parameter;

            if (pos == value.length() || value.charAt(pos) == ',' || value.charAt(pos) == ';') {
              pos++; // consume ',' or ';' (if necessary)
              parameter = null;
            } else {
              pos++; // consume '='
              pos = HeaderParser.skipWhitespace(value, pos);

              // quoted string
              if (pos < value.length() && value.charAt(pos) == '\"') {
                pos++; // consume '"' open quote
                int parameterStart = pos;
                pos = HeaderParser.skipUntil(value, pos, "\"");
                parameter = value.substring(parameterStart, pos);
                pos++; // consume '"' close quote (if necessary)

                // unquoted string
              } else {
                int parameterStart = pos;
                pos = HeaderParser.skipUntil(value, pos, ",;");
                parameter = value.substring(parameterStart, pos).trim();
              }
            }

            if ("no-cache".equalsIgnoreCase(directive)) {
              noCache = true;
            } else if ("no-store".equalsIgnoreCase(directive)) {
              noStore = true;
            } else if ("max-age".equalsIgnoreCase(directive)) {
              maxAgeSeconds = HeaderParser.parseSeconds(parameter, -1);
            } else if ("s-maxage".equalsIgnoreCase(directive)) {
              sMaxAgeSeconds = HeaderParser.parseSeconds(parameter, -1);
            } else if ("private".equalsIgnoreCase(directive)) {
              isPrivate = true;
            } else if ("public".equalsIgnoreCase(directive)) {
              isPublic = true;
            } else if ("must-revalidate".equalsIgnoreCase(directive)) {
              mustRevalidate = true;
            } else if ("max-stale".equalsIgnoreCase(directive)) {
              maxStaleSeconds = HeaderParser.parseSeconds(parameter, Integer.MAX_VALUE);
            } else if ("min-fresh".equalsIgnoreCase(directive)) {
              minFreshSeconds = HeaderParser.parseSeconds(parameter, -1);
            } else if ("only-if-cached".equalsIgnoreCase(directive)) {
              onlyIfCached = true;
            } else if ("no-transform".equalsIgnoreCase(directive)) {
              noTransform = true;
            }
          }
        }

        if (!canUseHeaderValue) {//canUseHeaderValue为true
          headerValue = null;
        }
        return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPrivate, isPublic,
            mustRevalidate, maxStaleSeconds, minFreshSeconds, onlyIfCached, noTransform, headerValue);//这里的onlyIfCached为false
  }
}



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容