天天看點

OkHttp3源碼解析--攔截器RetryAndFollowUpInterceptor

看一下OkHttp内置的第一個攔截器RetryAndFollowUpInterceptor:

RetryAndFollowUpInterceptor

public final class RetryAndFollowUpInterceptor implements Interceptor {
​
  private static final int MAX_FOLLOW_UPS = 20;  //最大的重定向次數
  private final OkHttpClient client;             //前面建立的OkHttpClient對象
  private final boolean forWebSocket;            //是否是webSocket
  private StreamAllocation streamAllocation;     //流配置設定器,在這個攔截器中初始化
  private Object callStackTrace;
  private volatile boolean canceled;             //請求是否被取消了
  
  //主要是傳入進來OkHttpClient對象
  public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
    this.client = client;
    this.forWebSocket = forWebSocket;
  }
  
  @Override public Response intercept(Chain chain) throws IOException {
    
    //***************擷取需要的對象Request,RealInterceptorChain,Call,StreamAllocation****************
    Request request = chain.request();                            //之前我們建立的Request對象
    RealInterceptorChain realChain = (RealInterceptorChain) chain;//建立的下一個攔截器鍊
    Call call = realChain.call();                                 //我們之前建立的RealCall
    EventListener eventListener = realChain.eventListener();
    //建立了一個StreamAllocation對象,持有全局的連接配接池,Address對象,Call對象
    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 {
        //**************開始進行請求*************************************************************
        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.
        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.
        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) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }
      
      
      //**********************解析回複的code,如果請求失敗,進行重定向*******************
      //解析response的code,判斷response的類型,看是否請求成功。
      Request followUp = followUpRequest(response);
      //如果請求成功,傳回followUp Request為null
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        //請求成功,傳回這個response
        return response;
      }
      //請求不成功,這個response關閉,釋放資源。
      closeQuietly(response.body());
      //followUpCount加1後判斷是否大于MAX_FOLLOW_UPS
      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());
      }
      //看response的伺服器位址和followUp Request的url對比,看位址是否相同,這涉及是否可以連接配接複用。
      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        //重新建立一個StreamAllocation對象
        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對象
      request = followUp;
      //記錄Response
      priorResponse = response;
    }
    
    /**
   * 取消執行請求
   */
  public void cancel() {
    canceled = true;
    StreamAllocation streamAllocation = this.streamAllocation;
    if (streamAllocation != null) streamAllocation.cancel();
  }
  //傳回是否被取消
  public boolean isCanceled() {
    return canceled;
  }
​
}
​
           

上述過程中出現了StreamAllocation對象的建立,以及調用release方法等。下面看一下StreamAllocation這個類:

StreamAllocation

public final class StreamAllocation {
  public final Address address;                  //請求位址資訊
  private RouteSelector.Selection routeSelection;//路由選擇器
  private Route route;                           //路由
  private final ConnectionPool connectionPool;   //全局連接配接池,在OkHttpClient對象建立過程中已經建立完成
  public final Call call;                        //請求Call
  public final EventListener eventListener;
  private final Object callStackTrace;
​
  // State guarded by connectionPool.
  private final RouteSelector routeSelector;
  private int refusedStreamCount;
  private RealConnection connection;            //真實建立的連接配接
  private boolean reportedAcquired;
  private boolean released;                     //資源是否被釋放
  private boolean canceled;                     //請求是否被取消
  private HttpCodec codec;                      //Http 流
  
  public StreamAllocation(ConnectionPool connectionPool, Address address, Call call,
      EventListener eventListener, Object callStackTrace) {
    this.connectionPool = connectionPool;
    this.address = address;
    this.call = call;
    this.eventListener = eventListener;
    this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
    this.callStackTrace = callStackTrace;
  }
  //釋放資源
  public void release() {
    Socket socket;
    Connection releasedConnection;
    synchronized (connectionPool) {
      releasedConnection = connection;
      socket = deallocate(false, true, false);//會關閉connection對象
      if (connection != null) releasedConnection = null;
    }
    closeQuietly(socket);
    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection);
    }
  }
}
           

後續會針對StreamAllocation專門進行更深入的介紹。

重定向功能是否開啟,在建立OkHttpClient的時候可以設定:

new OkHttpClient().newBuilder()
                  .followRedirects(false)  //禁制OkHttp的重定向操作,我們自己處理重定向
                  .followSslRedirects(false)//https的重定向也自己處理
​
           

簡單進行流程總結:

繼續閱讀