天天看點

OkHttp3源碼分析-RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor

有兩個功能,一個是重試,一個是重定向。如果

Call

被取消,

RetryAndFollowUpInterceptor

将抛出異常。

我們來一起分析一下。

public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
    	Request request = chain.request();
    	RealInterceptorChain realChain = (RealInterceptorChain) chain;
    	Call call = realChain.call();
    	EventListener eventListener = realChain.eventListener();

    	StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        	createAddress(request.url()), call, eventListener, callStackTrace);
    	this.streamAllocation = streamAllocation;
        ......
    }
}
           

上面代碼主要是建立

StreamAllocation

對象。後面還會再做分析,它用于協調

Connection

Stream

Call

public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        int followUpCount = 0;
    	Response priorResponse = null;
    	while (true) {
      		if (canceled) {
        		streamAllocation.release();
        		throw new IOException("Canceled");
      		}
            ·····
        }
    }
}
           

判斷是否取消了請求,如果是的就就直接抛出異常。

public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        int followUpCount = 0;
    	Response priorResponse = null;
    	while (true) {
          ......
      	  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(), streamAllocation, false, request)) {
              throw e.getFirstConnectException();
            }
            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, streamAllocation, requestSendStarted, request)) throw e;
            releaseConnection = false;
            continue;
          } finally {
            // We're throwing an unchecked exception. Release any resources.
            if (releaseConnection) {
              streamAllocation.streamFailed(null);
              streamAllocation.release();
            }
          }
          ······
        }
    }
}
           

循環處理請求。

  1. 嘗試着從責任鍊中擷取響應

    Response

  2. 如果出現異常

    RouteException

    IOException

    嘗試着去恢複,并繼續。
public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        int followUpCount = 0;
    	Response priorResponse = null;
    	while (true) {
            ......
      		if (priorResponse != null) {
        		response = response.newBuilder()
            	    .priorResponse(priorResponse.newBuilder()
                        .body(null)
                        .build())
            	    .build();
      		}
            ......
        }
    }
}
           

如果上次的請求的

Response

不為

null

,添加到目前的

Response

中。

public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        int followUpCount = 0;
    	Response priorResponse = null;
    	while (true) {
            ......
      		Request followUp;
      		try {
        		followUp = followUpRequest(response, streamAllocation.route());
      		} catch (IOException e) {
        		streamAllocation.release();
        		throw e;
      		}
             if (followUp == null) {
        		streamAllocation.release();
        		return response;
      		}
            ......
        }
    }
}
           

調用

followUpRequest

擷取下次請求

Request

followUpRequest

中包括重定向,認證錯誤等處理。如果

followUp

null

說明不能進行下次重試或者重定向了,直接傳回。

public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        int followUpCount = 0;
    	Response priorResponse = null;
    	while (true) {
            ......
      	  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);
            this.streamAllocation = streamAllocation;
          } 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;
        }
    }
}
           

先是關閉

response

body

接着就是判斷

followUpCount

是否超過了最大的

MAX_FOLLOW_UPS

,超過了直接抛出異常。

接着判斷

followUp

body

是否是

UnrepeatableRequestBody

的執行個體,如果是,直接抛出異常。

接着通過

sameConnection

方法判斷相應的

response

followUp

是否是同一個連接配接,如果是重定向二者不會相同,需要重新建立

StreamAllocation

最後是指派到相應的變量。繼續循環。