OkHttp網絡請求框架源碼解析二

 

OkHttp網絡請求框架源碼解析二

 

本篇講解OkHttp核心知識攔截器,共有5個攔截器,分別爲RetryAndFollowUpInterceptor,BridgeInterceptor, CacheInterceptor,ConnectInterceptor 和 CallServerInterceptor。下面一一講到。

//上一篇講到,作爲Runnable的 AsyncCall,最終的execute()方法爲RealCall裏面的execute()方法。
@Override protected void execute() {
     
        //獲取響應回來的數據response
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          
          //回調onFailure
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          
          //回調onResponse
          responseCallback.onResponse(RealCall.this, response);
        }
      
        ......
        
        //線程池執行完線程任務後,調用此方法.
        client.dispatcher().finished(this);
 
 
    }



//不管是同步請求,還是異步請求,都是通過getResponseWithInterceptorChain方法獲取響應。
Response getResponseWithInterceptorChain() throws IOException {

    List<Interceptor> interceptors = new ArrayList<>();

    //添加用戶自定義的攔截器
    interceptors.addAll(client.interceptors());

    ......

    //創建攔截器鏈    
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    
    //創建下一個攔截器鏈
    return chain.proceed(originalRequest);
  }


//chain.proceed()方法實現
public Response proceed() throws IOException {
    
    ......
    
    //創建下一個攔截器鏈 index+1
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);

    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }




第一個攔截器,重試重定向攔截器:RetryAndFollowUpInterceptor。

1.創建StreamAllocation對象;

2.調用RealInterceptorChain.proceed()方法進行網絡請求;

3.根據結果判斷是否重新請求;

4.調用下一個攔截器;


//RetryAndFollowUpInterceptor攔截器裏面的核心方法intercept(),
@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    //StreamAllocation 獲取建立Http連接的所有網絡組件
    //雖然在這裏被創建,但是這裏沒有使用。
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    ......

    //重試次數20次
     if (++followUpCount > 20) {
        streamAllocation.release();      
      }

}

第二個攔截器,橋接攔截器,BridgeInterceptor。

1.將request請求轉換成可以進行網絡訪問的請求;

2.進行網絡請求;

3.將響應response轉換成客戶度可以用的response。

//BridgeInterceptor橋接攔截器的核心方法intercept()
Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
   
      給Request添加各種頭部信息
      requestBuilder.header("Content-Type", contentType.toString());    

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
      }
    }

      ......

      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
      requestBuilder.header("Connection", "Keep-Alive");
    
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

      List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
      requestBuilder.header("Cookie", cookieHeader(cookies));
      requestBuilder.header("User-Agent", Version.userAgent());
    
    //從服務器獲取response
    Response networkResponse = chain.proceed(requestBuilder.build());

    //將服務器返回的數據頭部轉換成客戶端可以接收的形式
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), 
    networkResponse.headers());

第三個攔截器,緩存攔截器CacheInterceptor。

 //CacheInterceptor核心方法intercept
 @Override public Response intercept(Chain chain) throws IOException {
        
    //獲取緩存
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    //創建CacheStrategy
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(),         
    cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    //關於緩存命中率,如果使用了緩存,則hitCount++。
    cache.trackResponse(strategy);
    

    //第一種情況,如果不能使用網絡,並且沒有緩存,則OkHttp內部構建一個504響應作爲response。
    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();
    }

    //第二種情況,不能使用網絡,但是有緩存,則使用緩存作爲response。
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    //第三種情況,可以使用網絡,不管是否有緩存都會調用chain.proceed方法進行網絡請求。
    Response networkResponse = null;
    networkResponse = chain.proceed(networkRequest);
   
    //如果有緩存,並且服務器響應碼爲304,表示內容未被修改,則使用緩存作爲response.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        return response;
      } 
    }
    
    //如果沒有緩存,則將剛纔網絡請求的response作爲response.
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    //如果代碼能走到這裏,說明前面網絡請求的響應碼不是304,表示response被修改了,則更新緩存。
    if (cache != null) {
       
      //條件判斷
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        
        //存入新的緩存
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      //假如請求方式不是get,則刪除緩存,因爲OkHttp只對get方式的請求進行緩存。
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        cache.remove(networkRequest);
      }
    }

    return response;
  }

第四個攔截器,連接池攔截器,ConnectInterceptor。


//ConnectInterceptor整個類的代碼
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 streamAllocation = realChain.streamAllocation();
    

    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    
    //HttpCodec用來 編碼request,解碼response
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);

    //RealConnection 進行實際的網絡io傳輸
    RealConnection connection = streamAllocation.connection();

    //將重要的HttpCodec對象和RealConnection對象傳遞給後面的攔截器
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}


//streamAllocation.newStream方法的實現
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
   
      //通過findHealthyConnection方法獲取RealConnection
      RealConnection resultConnection = findHealthyConnection(connectTimeout,     
      readTimeout,writeTimeout,connectionRetryEnabled, doExtensiveHealthChecks);

      //通過RealConnection的newCodec方法獲取HttpCodec對象,最後返回。
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
   
  }


//findHealthyConnection方法的實現
private RealConnection findHealthyConnection(){
    while (true) {

      //循環裏面,通過findConnection()方法獲取RealConnection對象。然後返回。
      RealConnection candidate = findConnection(connectTimeout, readTimeout, 
      writeTimeout,
      connectionRetryEnabled);
      synchronized (connectionPool) {
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

    ......

  }


//findConnection()方法的實現
private RealConnection findConnection(){

    //先複用已有的連接,如果滿足條件,則返回
    RealConnection allocatedConnection = this.connection;
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }

    //如果不能複用,則從連接池connectionPool裏面獲取RealConnection對象。
    Internal.instance.get(connectionPool, address, this, null);
      if (connection != null) {
        return connection;
      }

    //調用RealConnection對象的connect()方法進行實際的網絡請求連接
    result.connect(connectTimeout, readTimeout, writeTimeout, 
    connectionRetryEnabled);

    //然後把上面的連接放入連接池connectionPool      
    Internal.instance.put(connectionPool, result);


}

第五個攔截器,CallServerInterceptor。

//CallInterceptor攔截器的核心方法實現 
 @Override public Response intercept(Chain chain) throws IOException {

    //5個重要的對象,第一個:攔截器鏈
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //5個重要的對象,第二個:HttpCodec編碼request,解碼response
    HttpCodec httpCodec = realChain.httpStream();
    //5個重要的對象,第三個:分配網絡請求組件
    StreamAllocation streamAllocation = realChain.streamAllocation();
    //5個重要的對象,第四個:真正的網絡請求
    RealConnection connection = (RealConnection) realChain.connection();
    //5個重要的對象,第五個:
    Request request = realChain.request();

    //第一步:寫請求頭。
    httpCodec.writeRequestHeaders(request);
    //第二步:寫請求體
    request.body().writeTo(bufferedRequestBody);
    //第三步:結束請求
    httpCodec.finishRequest();    
    //第四步:讀取response請求頭
    responseBuilder = httpCodec.readResponseHeaders(false);
    //第五步:讀取response請求體
    response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();



}


 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章