OKHTTP 之責(zé)任鏈理解

okhttp中一開始以為攔截器使用的是動態(tài)代理,類似Spring MVC里面的攔截器,當(dāng)然okhttp僅僅為底層框架是沒必要代理的,不過retrofit 使用的是動態(tài)代理,后來翻看源碼為責(zé)任鏈加遞歸調(diào)用

攔截器調(diào)用順序

RealCall.java

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)); //最底下是CallServerInterceptor 攔截器

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
  • 在配置 OkHttpClient 時設(shè)置的 interceptors;
  • 負(fù)責(zé)失敗重試以及重定向的 RetryAndFollowUpInterceptor;
  • 負(fù)責(zé)把用戶構(gòu)造的請求轉(zhuǎn)換為發(fā)送到服務(wù)器的請求、把服務(wù)器返回的 響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng)的 BridgeInterceptor;
  • 負(fù)責(zé)讀取緩存直接返回、更新緩存的 CacheInterceptor;
  • 負(fù)責(zé)和服務(wù)器建立連接的 ConnectInterceptor;
  • 配置 OkHttpClient 時設(shè)置的 networkInterceptors;
  • 負(fù)責(zé)向服務(wù)器發(fā)送請求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)的 CallServerInterceptor。

RealInterceptorChain.java

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !sameConnection(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
//索引+1 ,這里是使用了RealInterceptorChain的鏈 進(jìn)行遞歸管理的,而不是我們普通上的那種遞歸,這種使用方式值得考慮,
//這樣可以進(jìn)行一些判斷等處理,遞歸的是類,不是方法,很棒
    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");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }

CacheInterceptor.java


    // 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);  // 此處進(jìn)行了遞歸處理
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

CallServerInterceptor.java

在最后一個Intercept 里面是直接返回了response 而不是進(jìn)行繼續(xù)遞歸

 httpCodec.finishRequest();

    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder //  這里直接進(jìn)行了new操作
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;

總結(jié):

  • 首先定義攔截器一層層往下運(yùn)行,直到最底下的攔截器返回response ,一層層往上透傳,直到最上層,然后處理一些信息,這個很像事件傳遞機(jī)制,類比下
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • OkHttp源碼的samples的簡單使用的示例: public static void main(String....
    _warren閱讀 817評論 0 1
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請求時所使用到的各個請求庫的關(guān)系,以及 OkHttp3 的介紹。(如理解有誤,...
    小莊bb閱讀 1,213評論 0 4
  • 緩存的一般思路 下面是我理解的網(wǎng)絡(luò)請求框架的緩存基本實(shí)現(xiàn)。大致的過程是有緩存用緩存的數(shù)據(jù),沒緩存發(fā)起http請求取...
    原件閱讀 2,826評論 3 12
  • 下個路口還是沒有你 搞不懂自己為什么要答應(yīng) 如果這五個多月我仍然孤寂 生活不就是平淡自然而簡單 我這么多的小情緒 ...
    整個宇宙我最帥閱讀 165評論 0 2
  • 本來是要去吃夜宵的,但姚阿姨叫我過來坐下:看到那么都美食我就嘴饞了……忍不住就吃撐了,謝謝你!我們都好開心!
    王玉蓮_7c09閱讀 398評論 0 0