流行框架源碼分析(8)-OkHttp源碼分析

主目錄見(jiàn):Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
?OkHttp的知識(shí)點(diǎn)實(shí)在是不少优训,優(yōu)秀的思想也有很多戈次,這里只能說(shuō)盡量會(huì)提到但是沒(méi)有把握非常詳細(xì)兵迅,要讀懂Okhttp的代碼需要的知識(shí)還比較多算凿,大家做好準(zhǔn)備呀囱井,如果提到的知識(shí)不懂念恍,可以另外查查六剥,不然這篇文章就會(huì)太長(zhǎng)了,同時(shí)這里借鑒一張整體流程圖:

OkHttp流程圖

這張流程圖還是非常詳盡的峰伙,再結(jié)合我們代碼的解析疗疟,應(yīng)該能了解整體的過(guò)程,當(dāng)然中間還會(huì)說(shuō)明一些優(yōu)秀的思想词爬,如果要看一些類的詳細(xì)介紹也可以參考[官方的wiki]秃嗜。

一.目標(biāo)

?首先看OkHttp的源碼主要是為了了解網(wǎng)絡(luò)相關(guān)框架的一些機(jī)制,發(fā)散我們的思維顿膨,同時(shí)讓我們用這個(gè)框架的時(shí)候更能得心應(yīng)手锅锨,同時(shí)我們今天目標(biāo)有如下:
1.為后面的retrofit的講解做鋪墊,畢竟retrofit底層用的訪問(wèn)網(wǎng)絡(luò)框架是okhttp;
2.了解一些網(wǎng)絡(luò)框架優(yōu)秀的設(shè)計(jì)思想恋沃,以拓展我們的知識(shí)必搞。

二.源碼分析

首先我們這里也是跟前面的文章一樣,先來(lái)看看基礎(chǔ)的用法囊咏,我們這里舉個(gè)Get異步請(qǐng)求的基本使用:

    mOkHttpClient=new OkHttpClient();
    Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com");
    //可以省略恕洲,默認(rèn)是GET請(qǐng)求
    requestBuilder.method("GET",null);
    Request request = requestBuilder.build();
    Call mcall= mOkHttpClient.newCall(request);
    mcall.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (null != response.cacheResponse()) {
                String str = response.cacheResponse().toString();
            } else {
                response.body().string();
                String str = response.networkResponse().toString();
            }
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), "請(qǐng)求成功", Toast.LENGTH_SHORT).show();
                }
            });
        }
    });
}

我們看到這里首先是創(chuàng)建了一個(gè)Request對(duì)象然后傳給OkHttpClient的newCall方法塔橡,然后調(diào)用Call對(duì)象的enqueue進(jìn)行異步請(qǐng)求,如果是同步請(qǐng)求就是execute()方法霜第。

1.OkHttpClient newCall

我們看到程序前面調(diào)用Request的Builder方法葛家,構(gòu)建了一個(gè)請(qǐng)求的Request,然后會(huì)把這個(gè)Request對(duì)象傳給OkHttpClient的newCall方法泌类,所以我們來(lái)看下這個(gè)方法:

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

我們看到這個(gè)方法調(diào)用了RealCall的newRealCall()方法癞谒,我們繼續(xù)看這個(gè)方法干了什么:

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

我們看到這個(gè)方法里面主要是new出了一個(gè)RealCall實(shí)例,我們看下這個(gè)構(gòu)造函數(shù):

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

我們看到這個(gè)構(gòu)造函數(shù)主要是賦值刃榨,同時(shí)實(shí)例化了一個(gè)攔截器RetryAndFollowUpInterceptor弹砚,然后前面程序又賦值了一個(gè)eventListener對(duì)象,這個(gè)監(jiān)聽(tīng)主要是監(jiān)聽(tīng)請(qǐng)求網(wǎng)絡(luò)的整個(gè)過(guò)程的枢希,系統(tǒng)已經(jīng)有一個(gè)RecordingEventListener了桌吃,但是沒(méi)有使用,如果需要你可以在OkHttpClient的Builder中設(shè)置苞轿,例如:

   client = defaultClient().newBuilder()
        .dns(singleDns)
        .eventListener(listener)
        .build();

到這里我們已經(jīng)得到一個(gè)RealCall對(duì)象了茅诱,我們看前面的流程圖也知道,我們接下來(lái)如果是異步的話就要調(diào)用RealCall的enqueue()方法呕屎,如果是同步請(qǐng)求就要調(diào)用execute()方法让簿,我們這里就以enqueue()方法為例。

2.RealCall enqueue

我們看到我們得到一個(gè)RealCall對(duì)象秀睛,然后會(huì)根據(jù)同步還是異步請(qǐng)求來(lái)分別調(diào)用不同方法尔当,這里我們看看enqueue方法:

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
//這個(gè)主要是獲取堆棧信息并且給RetryAndFollowUpInterceptor攔截器
    captureCallStackTrace();
//調(diào)用監(jiān)聽(tīng)的方法
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

我們看到這個(gè)函數(shù)最后調(diào)用Dispatcher中的enqueue方法。這個(gè)方法里面?zhèn)魅胍粋€(gè)AsyncCall對(duì)象蹂安,我們先來(lái)看下這個(gè)對(duì)象是什么:

final class AsyncCall extends NamedRunnable {
.......
  }

這個(gè)類是一個(gè)NamedRunnable對(duì)象椭迎,NamedRunnable實(shí)現(xiàn)了Runnable接口,然后會(huì)在run方法里面調(diào)用execute方法田盈,這個(gè)execute方法就在AsyncCall 中實(shí)現(xiàn)了畜号。等會(huì)會(huì)執(zhí)行到。那么我們先來(lái)看下Dispatcher的enqueue方法允瞧。

3.Dispatcher enqueue

這里的Dispatcher 是個(gè)線程調(diào)度池简软,他有以下幾個(gè)作用:
1).調(diào)度線程池Disptcher實(shí)現(xiàn)了高并發(fā),低阻塞的實(shí)現(xiàn);
2).采用Deque作為緩存述暂,先進(jìn)先出的順序執(zhí)行;
3).任務(wù)在try/finally中調(diào)用了finished函數(shù)痹升,控制任務(wù)隊(duì)列的執(zhí)行順序,而不是采用鎖畦韭,減少了編碼復(fù)雜性提高性能疼蛾。
知道這些概念之后我們來(lái)分析源碼,來(lái)鞏固這些是不是可以在代碼中找到蛛絲馬跡艺配,我們跟進(jìn)enqueue方法:

synchronized void enqueue(AsyncCall call) {
//判斷正在執(zhí)行的請(qǐng)求數(shù)量是否操作最大請(qǐng)求數(shù)且請(qǐng)求是否超過(guò)單個(gè)主機(jī)最大的請(qǐng)求數(shù)量
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加進(jìn)正在執(zhí)行的請(qǐng)求隊(duì)列察郁。包括正在執(zhí)行的異步請(qǐng)求厉颤,包含已經(jīng)取消但未執(zhí)行完的請(qǐng)求
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
// 添加進(jìn)準(zhǔn)備執(zhí)行的異步請(qǐng)求隊(duì)列
      readyAsyncCalls.add(call);
    }
  }

我們看到這是如果正在執(zhí)行的請(qǐng)求數(shù)量小于操作最大請(qǐng)求數(shù)且請(qǐng)求小于單個(gè)主機(jī)最大的請(qǐng)求數(shù)量則添加進(jìn)正在執(zhí)行的請(qǐng)求隊(duì)列中去酒繁,否則的話則添加進(jìn)準(zhǔn)備執(zhí)行的異步請(qǐng)求隊(duì)列中去呼寸,這樣減少了阻塞火的。我們看到如果條件符合會(huì)調(diào)用executorService()方法:

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

這個(gè)地方使用了單例的線程池,線程池里面的幾個(gè)參數(shù)這里解釋下哈:

  • 0(corePoolSize):表示核心線程池的數(shù)量為 0麦轰,空閑一段時(shí)間后所有線程將全部被銷毀眷柔。
  • Integer.MAX_VALUE(maximumPoolSize):最大線程數(shù),當(dāng)任務(wù)進(jìn)來(lái)時(shí)可以擴(kuò)充的線程最大值原朝,相當(dāng)于無(wú)限大。
  • 60(keepAliveTime): 當(dāng)線程數(shù)大于corePoolSize時(shí)镶苞,多余的空閑線程的最大存活時(shí)間喳坠。
  • TimeUnit.SECONDS:存活時(shí)間的單位是秒。
  • new SynchronousQueue<Runnable>():這是個(gè)不存儲(chǔ)元素的阻塞隊(duì)列茂蚓,先進(jìn)先出壕鹉,負(fù)責(zé)把生產(chǎn)者線程處理的數(shù)據(jù)直接傳遞給消費(fèi)者線程。
  • Util.threadFactory("OkHttp Dispatcher", false):?jiǎn)蝹€(gè)線程的工廠 聋涨。
    總的來(lái)說(shuō)晾浴,就是有多少個(gè)并發(fā)請(qǐng)求就創(chuàng)建幾個(gè)線程,當(dāng)請(qǐng)求完畢之后牍白,會(huì)在60s時(shí)間內(nèi)回收關(guān)閉脊凰。得到這個(gè)線程池之后會(huì)調(diào)用execute執(zhí)行:
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
//調(diào)用攔截器的責(zé)任鏈進(jìn)行網(wǎng)絡(luò)請(qǐng)求
        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 {
//調(diào)用dispatcher進(jìn)行finished
        client.dispatcher().finished(this);
      }
    }

我們看到我們這里有一句非常重要的代碼getResponseWithInterceptorChain,這里面就有我們一系列的攔截器了茂腥,我們可以直接跟進(jìn)這個(gè)方法狸涌。

4. getResponseWithInterceptorChain

所有的很多核心功能都在這里面了,所以我們看見(jiàn)這個(gè)方法做了些什么:

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

我們看到這里面首先會(huì)添加我們自己設(shè)置的攔截器最岗,然后會(huì)分別添加retryAndFollowUpInterceptor(負(fù)責(zé)失敗重試以及重定向)帕胆,BridgeInterceptor(負(fù)責(zé)把用戶構(gòu)造的請(qǐng)求轉(zhuǎn)換為發(fā)送到服務(wù)器的請(qǐng)求、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng))般渡,CacheInterceptor(負(fù)責(zé)讀取緩存直接返回懒豹、更新緩存),ConnectInterceptor(負(fù)責(zé)和服務(wù)器建立連接)驯用,networkInterceptors(配置 OkHttpClient 時(shí)設(shè)置的 networkInterceptors)脸秽,CallServerInterceptor(負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù))晨汹。然后實(shí)例化一個(gè)RealInterceptorChain對(duì)象豹储,調(diào)用proceed方法進(jìn)行鏈?zhǔn)秸{(diào)用。我們跟進(jìn)proceed方法:

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

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }
.......
    return response;
  }

我們看到這段代碼里面實(shí)例化一個(gè)index+1的RealInterceptorChain淘这,然后獲取相應(yīng)index的攔截器剥扣,調(diào)用攔截器的intercept方法巩剖。

5.retryAndFollowUpInterceptor

首先如果我們客戶端沒(méi)有設(shè)置攔截器,這個(gè)就是默認(rèn)的第一個(gè)攔截器了钠怯,那么程序會(huì)調(diào)用這個(gè)攔截器的intercept方法:

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
//實(shí)例化StreamAllocation對(duì)象(Socket管理)
    streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
        call, eventListener, callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
//這里直接調(diào)用下一個(gè)攔截器來(lái)返回請(qǐng)求結(jié)果佳魔,然后捕獲異常
        response = realChain.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.
//通過(guò)路線連接失敗,請(qǐng)求將不會(huì)再發(fā)送
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
// 與服務(wù)器嘗試通信失敗晦炊,請(qǐng)求不會(huì)再發(fā)送鞠鲜。
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, 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.
      if (priorResponse != null) {
//如果前面請(qǐng)求還沒(méi)有請(qǐng)求結(jié)果断国,則我們構(gòu)造一個(gè)空的請(qǐng)求結(jié)果body內(nèi)容為空
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }
//根據(jù)請(qǐng)求返回的狀態(tài)碼贤姆,會(huì)加上驗(yàn)證或者重定向,或者超時(shí)的處理
      Request followUp = followUpRequest(response);

      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) {
        streamAllocation.release();
        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()), call, eventListener, callStackTrace);
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }

我們看到我們程序首先會(huì)實(shí)例化一個(gè)StreamAllocation對(duì)象稳衬,這個(gè)對(duì)象專門(mén)負(fù)責(zé)socket管理的霞捡,這個(gè)對(duì)象里面的參數(shù)分別為連接池connectionPool,還有調(diào)用了createAddress方法薄疚,我們看下這個(gè)方法做了什么:

private Address createAddress(HttpUrl url) {
    SSLSocketFactory sslSocketFactory = null;
    HostnameVerifier hostnameVerifier = null;
    CertificatePinner certificatePinner = null;
    if (url.isHttps()) {
      sslSocketFactory = client.sslSocketFactory();
      hostnameVerifier = client.hostnameVerifier();
      certificatePinner = client.certificatePinner();
    }

    return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
        sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
        client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
  }

我們看到這個(gè)方法里面主要是實(shí)例化了Address對(duì)象碧信,這個(gè)對(duì)象主要存儲(chǔ)用來(lái)連接服務(wù)器所需的一些信息,包括主機(jī)街夭,端口號(hào)砰碴,dns,以及協(xié)議板丽,代理等信息呈枉。程序后面直接調(diào)用了下一個(gè)攔截器,所以我們先來(lái)看看下一個(gè)攔截器是干了什么檐什,下一個(gè)攔截器是BridgeInterceptor碴卧。

6.BridgeInterceptor

我們知道這個(gè)攔截器主要是負(fù)責(zé)把用戶構(gòu)造的請(qǐng)求轉(zhuǎn)換為發(fā)送到服務(wù)器的請(qǐng)求、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng)的 乃正。我們直接來(lái)看intercept方法:

@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

//這里主要取出http請(qǐng)求報(bào)文的請(qǐng)求數(shù)據(jù)內(nèi)容
    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
//往requestBuilder中添加請(qǐng)求頭Content-Type內(nèi)容
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
//往requestBuilder中添加請(qǐng)求頭Content-Length內(nèi)容
        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中添加請(qǐng)求頭Host內(nèi)容
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
//往requestBuilder中添加請(qǐng)求頭Connection內(nèi)容
      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 && userRequest.header("Range") == null) {
      transparentGzip = true;
//是否支持gzip壓縮住册,如果有則添加Accept-Encoding = gzip
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
//往requestBuilder中添加請(qǐng)求頭Cookie內(nèi)容
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
//往requestBuilder中添加請(qǐng)求頭User-Agent內(nèi)容
      requestBuilder.header("User-Agent", Version.userAgent());
    }

//調(diào)用下一個(gè)攔截器,將請(qǐng)求數(shù)據(jù)傳過(guò)去
    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//根據(jù)下面攔截器處理的請(qǐng)求返回?cái)?shù)據(jù)來(lái)構(gòu)建一個(gè)新的Response.Builder對(duì)象
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
//如果支持gzip壓縮則做相應(yīng)的處理
    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)));
    }
//最后返回一個(gè)Response對(duì)象
    return responseBuilder.build();
  }

我們看到這個(gè)攔截器主要的功能就是拼接request的請(qǐng)求頭瓮具,然后傳給下一個(gè)攔截器進(jìn)行處理荧飞,根據(jù)下面攔截器返回的結(jié)果返回一個(gè)response對(duì)象給上面的攔截器。所以我們接下來(lái)看下一個(gè)攔截器CacheInterceptor名党。

7.CacheInterceptor

這個(gè)攔截器主要是負(fù)責(zé)讀取緩存直接返回叹阔、更新緩存,我們同樣來(lái)看intercept做了什么:

 @Override public Response intercept(Chain chain) throws IOException {
//cacheCandidate從disklurcache中獲取
//request的url被md5序列化為key,進(jìn)行緩存查詢
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
////請(qǐng)求與緩存
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }
//如果緩存命中但是cacheResponse 為null則說(shuō)明這個(gè)緩存非法传睹,關(guān)閉
    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.
//如果網(wǎng)絡(luò)請(qǐng)求request為空且返回的緩存為空耳幢,則就是出錯(cuò)了request不正確
    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.
//如果請(qǐng)求為空我們就直接返回緩存
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
//調(diào)用下一個(gè)攔截器進(jìn)行請(qǐng)求
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//如果緩存不為空,且網(wǎng)絡(luò)請(qǐng)求返回的請(qǐng)求碼為未修改則合并header信息,存儲(chǔ)相應(yīng)信息
        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();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
//根據(jù)網(wǎng)絡(luò)返回的response和緩存的response來(lái)構(gòu)造一個(gè)Response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

我們看到Factory.get()就是關(guān)鍵的緩存策略判斷了睛藻,我們進(jìn)入get()方法启上,我們發(fā)現(xiàn)實(shí)際的代碼在getCandidate方法中:

  private CacheStrategy getCandidate() {
      // No cached response.
//如果緩存沒(méi)有命中(即null),網(wǎng)絡(luò)請(qǐng)求也不需要加緩存Header了
      if (cacheResponse == null) {
 //沒(méi)有緩存的網(wǎng)絡(luò)請(qǐng)求則上面代碼可知是直接訪問(wèn)
        return new CacheStrategy(request, null);
      }

      // Drop the cached response if it's missing a required handshake.
//如果緩存的TLS握手信息丟失,返回進(jìn)行直接連接
      if (request.isHttps() && cacheResponse.handshake() == null) {
//也是直接訪問(wèn)
        return new CacheStrategy(request, null);
      }

      // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
////檢測(cè)response的狀態(tài)碼,Expired時(shí)間,是否有no-cache標(biāo)簽
      if (!isCacheable(cacheResponse, request)) {
//直接訪問(wèn)
        return new CacheStrategy(request, null);
      }

////如果請(qǐng)求報(bào)文使用了`no-cache`標(biāo)簽(這個(gè)只可能是開(kāi)發(fā)者故意添加的)
  //或者有ETag/Since標(biāo)簽(也就是條件GET請(qǐng)求)
      CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
 //直接連接,把緩存判斷交給服務(wù)器
        return new CacheStrategy(request, null);
      }

//這個(gè)是新的擴(kuò)展屬性表示響應(yīng)內(nèi)容將一直不會(huì)改變,它和max-age是對(duì)緩存生命周期控制的互補(bǔ)性屬性
      CacheControl responseCaching = cacheResponse.cacheControl();
      if (responseCaching.immutable()) {
//則直接返回緩存的信息店印,不進(jìn)行請(qǐng)求
        return new CacheStrategy(null, cacheResponse);
      }

 //根據(jù)RFC協(xié)議計(jì)算
  //計(jì)算當(dāng)前age的時(shí)間戳
  //now - sent + age (s)
      long ageMillis = cacheResponseAge();
//大部分情況服務(wù)器設(shè)置為max-age
      long freshMillis = computeFreshnessLifetime();

      if (requestCaching.maxAgeSeconds() != -1) {
//大部分情況下是取max-age
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
//大部分情況下設(shè)置是0
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }

      long maxStaleMillis = 0;
//ParseHeader中的緩存控制信息
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
//設(shè)置最大過(guò)期時(shí)間,一般設(shè)置為0
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }
 //緩存在過(guò)期時(shí)間內(nèi),可以使用
  //大部分情況下是進(jìn)行如下判斷
  //now - sent + age + 0 < max-age + 0
      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
//返回上次的緩存
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
 //緩存失效, 如果有etag等信息
  //進(jìn)行發(fā)送`conditional`請(qǐng)求,交給服務(wù)器處理
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }

      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

我們可以看到冈在,okhttp的緩存策略主要就是根據(jù)請(qǐng)求頭和服務(wù)器的Header來(lái)控制的,自己不需要特殊的處理按摘。當(dāng)然我們前面看到了包券,我們可以通過(guò)設(shè)置自己的緩存攔截器來(lái)實(shí)現(xiàn)緩存,不過(guò)這樣的話這里就不符合RFC協(xié)議標(biāo)準(zhǔn)了炫贤。我們可以通過(guò)在服務(wù)器配置恰當(dāng)?shù)木彺娌呗詠?lái)減少http請(qǐng)求溅固。我們這里通過(guò)一系列的緩存判斷之后還是會(huì)講request請(qǐng)求交給下一個(gè)攔截器來(lái)處理。下一個(gè)攔截器這個(gè)地方是ConnectInterceptor兰珍。

8.ConnectInterceptor

這個(gè)攔截器主要負(fù)責(zé)和服務(wù)器建立連接发魄,socket的連接相關(guān)內(nèi)容,這里也是來(lái)看intercept方法:

@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, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

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

這里我們看到這里得到了一個(gè)StreamAllocation 對(duì)象俩垃,這個(gè)對(duì)象的實(shí)例化在retryAndFollowUpInterceptor攔截器中已經(jīng)實(shí)例化了。我們看到這里調(diào)用了StreamAllocation對(duì)象的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);
    }
  }

我們看到這個(gè)方法又調(diào)用了findHealthyConnection方法汰寓,我們繼續(xù)跟進(jìn)去看下:

 private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          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;
    }
  }

我去口柳。。有滑。這個(gè)里面又調(diào)用了findConnection方法跃闹,同樣的,我們繼續(xù)跟進(jìn)去:

  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;
//如果沒(méi)有新的連接則釋放連接
      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.
//從緩存池里面得到一個(gè)連接
        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;
//選擇一個(gè)新的路由
    if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
      newRouteSelection = true;
      routeSelection = routeSelector.next();
    }

    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");
//嘗試從新的路由選擇器中找到合適的路由連接來(lái)從緩存池中找出有沒(méi)有連接存在
      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) {
//如果連接地址未在緩存池中找到則創(chuàng)建一個(gè)新的連接RealConnection
        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.
//跟服務(wù)器進(jìn)行TCP+TLS 的握手
    result.connect(
        connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;

      // Pool the connection.
//把新的連接放進(jìn)連接池中去
      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;
  }

這個(gè)類主要就是從緩存池中根據(jù)address查找是不是已經(jīng)有這么一個(gè)連接了毛好,有的話返回望艺,然后判斷是不是需要一個(gè)路由選擇器,這里的next()就是選擇線路肌访,選擇合適的路由和address在緩沖池中再查找合適的連接找默。我們就不具體跟進(jìn)代碼里面了,實(shí)在是有點(diǎn)多吼驶。我們說(shuō)下這個(gè)過(guò)程:
如果Proxy為null:
1.在構(gòu)造函數(shù)中設(shè)置代理為Proxy.NO_PROXY
2.如果緩存中的lastInetSocketAddress為空惩激,就通過(guò)DNS(默認(rèn)是Dns.SYSTEM,包裝了jdk自帶的lookup函數(shù))查詢蟹演,并保存結(jié)果风钻,注意結(jié)果是數(shù)組,即一個(gè)域名有多個(gè)IP酒请,這就是自動(dòng)重連的來(lái)源
3.如果還沒(méi)有查詢到就遞歸調(diào)用next查詢骡技,直到查到為止
4.一切next都沒(méi)有枚舉到,拋出NoSuchElementException羞反,退出(這個(gè)幾乎見(jiàn)不到)
如果Proxy為HTTP:
1.設(shè)置socket的ip為代理地址的ip
2.設(shè)置socket的端口為代理地址的端口
3.一切next都沒(méi)有枚舉到布朦,拋出NoSuchElementException囤萤,退出
到這里我們獲取到我們的Connection,然后就會(huì)調(diào)用RealConnection的connect方法:

 public void connect(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled, Call call, EventListener eventListener) {
    if (protocol != null) throw new IllegalStateException("already connected");

    RouteException routeException = null;
    List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
    ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
......
    while (true) {
      try {
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
          if (rawSocket == null) {
            // We were unable to connect the tunnel but properly closed down our resources.
            break;
          }
        } else {
          connectSocket(connectTimeout, readTimeout, call, eventListener);
        }
        establishProtocol(connectionSpecSelector, call, eventListener);
        eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
        break;
      } catch (IOException e) {
.......
     }

    if (route.requiresTunnel() && rawSocket == null) {
      ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
          + MAX_TUNNEL_ATTEMPTS);
      throw new RouteException(exception);
    }

    if (http2Connection != null) {
      synchronized (connectionPool) {
        allocationLimit = http2Connection.maxConcurrentStreams();
      }
    }
  }

這里面的connectTunnel和connectSocket方法(主要是前面那個(gè)方法是如果存在TLS,就根據(jù)SSL版本與證書(shū)進(jìn)行安全握手)都會(huì)調(diào)用到connectSocket方法喝滞,這個(gè)方法我們看下:

private void connectSocket(int connectTimeout, int readTimeout, Call call,
      EventListener eventListener) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket()
        : new Socket(proxy);

    eventListener.connectStart(call, route.socketAddress(), proxy);
    rawSocket.setSoTimeout(readTimeout);
    try {
//調(diào)用Platform.get().connectSocket選擇當(dāng)前平臺(tái)Runtime下最好的socket庫(kù)進(jìn)行握手阁将,如果存在TLS,就根據(jù)SSL版本與證書(shū)進(jìn)行安全握手
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
      ce.initCause(e);
      throw ce;
    }

    // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
    // More details:
    // https://github.com/square/okhttp/issues/3245
    // https://android-review.googlesource.com/#/c/271775/
    try {
//利用okio庫(kù)進(jìn)行讀寫(xiě)操作
      source = Okio.buffer(Okio.source(rawSocket));
      sink = Okio.buffer(Okio.sink(rawSocket));
    } catch (NullPointerException npe) {
      if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
        throw new IOException(npe);
      }
    }
  }

這里面的代碼主要跟服務(wù)器的socket連接右遭,然后在Http1Codec中做盅,它利用 Okio 對(duì)Socket的讀寫(xiě)操作進(jìn)行封裝,Okio 以后有機(jī)會(huì)再進(jìn)行分析窘哈,現(xiàn)在讓我們對(duì)它們保持一個(gè)簡(jiǎn)單地認(rèn)識(shí):它對(duì)java.io和java.nio進(jìn)行了封裝吹榴,讓我們更便捷高效的進(jìn)行 IO 操作。好了滚婉,到這里我們與服務(wù)器的連接已經(jīng)完成了图筹,當(dāng)然這里面還有連接池的一些管理。我們就暫時(shí)不說(shuō)明让腹。我們接下來(lái)講下一個(gè)攔截器CallServerInterceptor远剩。

9.CallServerInterceptor

這個(gè)攔截器主要是負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)骇窍,我們直接來(lái)看intercept方法:

 @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());
//將請(qǐng)求的頭部轉(zhuǎn)化為byte格式
    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));
//將請(qǐng)求報(bào)文中的請(qǐng)求數(shù)據(jù)用okio庫(kù)寫(xiě)到bufferedRequestBody 中去
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//然后寫(xiě)入到request的body中
        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);
    }
//根據(jù)網(wǎng)絡(luò)請(qǐng)求得到Response對(duì)象
    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();
.......
    return response;
  }

我們看到這里會(huì)請(qǐng)求網(wǎng)絡(luò)然后返回網(wǎng)絡(luò)的返回結(jié)果瓜晤,因?yàn)檫@些攔截器是用了責(zé)任鏈的設(shè)計(jì)模式,所以腹纳,再往下傳到這個(gè)攔截器完成之后痢掠,然后response的結(jié)果會(huì)往上傳,然后每個(gè)攔截器再對(duì)結(jié)果進(jìn)行相應(yīng)的處理嘲恍。如果請(qǐng)求的response失敗足画,我們看到我們第一個(gè)攔截器會(huì)進(jìn)行重試等操作處理,所以其實(shí)所有的核心操作都在各個(gè)攔截器中佃牛。
總結(jié):今天講okhttp淹辞,自我感覺(jué)有些細(xì)節(jié)講的不是很透,時(shí)間也寫(xiě)的比較急促俘侠,如果有不好的地方希望提出桑涎。到這里結(jié)合那張圖應(yīng)該整體流程是完整的,不過(guò)有些緩存池兼贡,任務(wù)隊(duì)列攻冷,復(fù)用連接池等等知識(shí)沒(méi)有提的非常詳細(xì)。如果有興趣可以一起交流遍希。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末等曼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌禁谦,老刑警劉巖胁黑,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異州泊,居然都是意外死亡丧蘸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)遥皂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)力喷,“玉大人,你說(shuō)我怎么就攤上這事演训〉苊希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵样悟,是天一觀的道長(zhǎng)拂募。 經(jīng)常有香客問(wèn)我,道長(zhǎng)窟她,這世上最難降的妖魔是什么陈症? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮震糖,結(jié)果婚禮上爬凑,老公的妹妹穿的比我還像新娘。我一直安慰自己试伙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布于样。 她就那樣靜靜地躺著疏叨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穿剖。 梳的紋絲不亂的頭發(fā)上蚤蔓,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音糊余,去河邊找鬼秀又。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贬芥,可吹牛的內(nèi)容都是我干的吐辙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蘸劈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昏苏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贤惯,失蹤者是張志新(化名)和其女友劉穎洼专,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體孵构,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屁商,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颈墅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜡镶。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖精盅,靈堂內(nèi)的尸體忽然破棺而出帽哑,到底是詐尸還是另有隱情,我是刑警寧澤叹俏,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布妻枕,位于F島的核電站,受9級(jí)特大地震影響粘驰,放射性物質(zhì)發(fā)生泄漏屡谐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一蝌数、第九天 我趴在偏房一處隱蔽的房頂上張望愕掏。 院中可真熱鬧,春花似錦顶伞、人聲如沸饵撑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)滑潘。三九已至,卻和暖如春锨咙,著一層夾襖步出監(jiān)牢的瞬間语卤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工酪刀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粹舵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓骂倘,卻偏偏與公主長(zhǎng)得像眼滤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子历涝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Okhttp使用指南與源碼分析 標(biāo)簽(空格分隔): Android 使用指南篇# 為什么使用okhttp### A...
    背影殺手不太冷閱讀 8,154評(píng)論 2 119
  • 用OkHttp很久了柠偶,也看了很多人寫(xiě)的源碼分析情妖,在這里結(jié)合自己的感悟,記錄一下對(duì)OkHttp源碼理解的幾點(diǎn)心得诱担。 ...
    藍(lán)灰_q閱讀 4,282評(píng)論 4 34
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理毡证,服務(wù)發(fā)現(xiàn),斷路器蔫仙,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • **版權(quán)聲明:本文為小斑馬偉原創(chuàng)文章料睛,轉(zhuǎn)載請(qǐng)注明出處! 一、 enqueue源碼分析 當(dāng)我們要進(jìn)行網(wǎng)絡(luò)請(qǐng)求的時(shí)候摇邦,...
    ZebraWei閱讀 420評(píng)論 0 1
  • 簡(jiǎn)約生活
    summer0559閱讀 153評(píng)論 0 0