Android OkhttpInterceptor 筆記:RetryAndFollowUpInterceptor

Anroid OKhttp筆記1 流程分析
Android OkhttpInterceptor 筆記:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 筆記:BridgeInterceptor
Android OkhttpInterceptor 筆記:ConnectInterceptor
Android OkhttpInterceptor 筆記:CacheInterceptor
Android OkhttpInterceptor 筆記:CallServerInterceptor
Android Okhttp筆記:ConnectionPool
Android Okhttp3:Dispatcher分析筆記


一、okhttp 內(nèi)置攔截器

本流程都是先創(chuàng)建一個OkHttpClient對象,然后通過Request.Builder()創(chuàng)建一個Request對象,OkHttpClient對象調(diào)用newCall()并傳入Request對象就能獲得一個Call對象。然后獲取一個真正用來進行請求的Call RealCall.execute :

final class RealCall implements Call {
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
}
@Override 
public Response execute() throws IOException {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    try {
        //進行網(wǎng)絡(luò)請求
        client.dispatcher().executed(this);
        //經(jīng)過一層層網(wǎng)絡(luò)攔截器之后,獲取網(wǎng)絡(luò)請求的返回值
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
    } finally {
        client.dispatcher().finished(this);
    }
  }
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //Application攔截器
    interceptors.addAll(client.interceptors());
    //重定向和失敗后重新請求攔截器
    interceptors.add(retryAndFollowUpInterceptor);
    //網(wǎng)橋攔截器,顧名思義client和Server之前的橋梁
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //緩存處理攔截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //Socket層的握手鏈接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        //網(wǎng)絡(luò)攔截器
        interceptors.addAll(client.networkInterceptors());
    }
    //client和Server之前的讀寫操作
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //責(zé)任鏈開始執(zhí)行
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
}

okhttp的攔截器就是將整個請求網(wǎng)絡(luò)的過程的每一步都封裝在不同的Interceptor里, 簡單點說就是把一個List里的Interceptor都順序執(zhí)行一遍

重試及重定向攔截器 RetryAndFollowUpInterceptor 負責(zé)請求的重試和重定向

橋接攔截器 BridgeInterceptor 給請求添加對應(yīng)的 header 信息,處理響應(yīng)結(jié)果的 header 信息

緩存攔截器 CacheInterceptor 根據(jù)當(dāng)前獲取的狀態(tài)選擇 網(wǎng)絡(luò)請求 、讀取緩存、更新緩存。

連接攔截器 ConnectInterceptor 建立 http 連接。

讀寫攔截器 CallServerInterceptor 通過連接好的通道進行數(shù)據(jù)的交換;
正常的一個okhttp網(wǎng)絡(luò)請求都走了哪些攔截器:

圖片來源于網(wǎng)絡(luò)

二、RetryAndFollowUpInterceptor核心代碼

This interceptor recovers from failures and follows redirects as necessary
網(wǎng)絡(luò)請求失敗后,在一些必要的條件下,會重新進行網(wǎng)絡(luò)請求。

大量代碼就不寫了,核心部分

      @Override 
 public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//...省略部分代碼
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
     //...省略部分代碼
  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();
    }
  }

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


  Request followUp;
  try {
    //判斷是否進行重新請求
    followUp = followUpRequest(response, streamAllocation.route());
  } catch (IOException e) {
    streamAllocation.release();
    throw e;
  }

  if (followUp == null) {
    //如果為空,則釋放資源,不空則繼續(xù)下一次請求
    streamAllocation.release();
    return response;
  }

  request = followUp;
  priorResponse = response;
}
}

三、解讀

1.正常流程

response = realChain.proceed(request, streamAllocation, null, null)
去網(wǎng)絡(luò)獲取到response并傳遞給下一級攔截器,然后沒有其他情況 ,followUpRequest==null 就返回response;

2其他

followUpRequest(response, streamAllocation.route());方法看會不會返回一個新的Request,若返回需要重定向,也就是拿新的Request去獲取Response,當(dāng)然這里有一個最大重定向次數(shù)限制:

   private static final int MAX_FOLLOW_UPS = 20;

3.異常情況

當(dāng)發(fā)生RouteException時候,并且 recover為false的時候,這時候會跳出循環(huán),然后拋出去異常,并回調(diào)callback.onFailure(Exception e),返回給UI層進行處理 ,假如說沒有開數(shù)據(jù)流量的情況下,去請求網(wǎng)絡(luò),則會拋出該異常。
第二種:當(dāng)發(fā)生IOException時候,recover為false的時候,則hrow exception,中斷死循環(huán)的操作,

4.recover方法

   private boolean recover(IOException e, boolean routeException, Request userRequest) {
    streamAllocation.streamFailed(e);
    //1.判斷 OkHttpClient 是否支持失敗重連的機制
    // The application layer has forbidden retries.
    if (!client.retryOnConnectionFailure()) return false;
    // 在該方法中傳入的 routeException值 為 true
    // We can't send the request body again.
    if (!routeException && userRequest.body() instanceof UnrepeatableRequestBody) return false;
    //2.isRecoverable 檢測該異常是否是致命的。
    // This exception is fatal.
    if (!isRecoverable(e, routeException)) return false;
    // No more routes to attempt.
    //3.是否有更多的路線
    if (!streamAllocation.hasMoreRoutes()) return false;
    // For failure recovery, use the same route selector with a new connection.
    return true;
  }

recover 方法主要做了以下幾件事:

1判斷 OkHttpClient 是否支持失敗重連的機制;
如果不支持重連,就表示請求失敗就失敗了,不能再重試了。

2請求Request出錯不能繼續(xù)使用

3.是否可以恢復(fù)的isRecoverable 方法異常檢測
在該方法中會檢測異常是否為嚴重異常,嚴重異常就不要進行重連了,下面檢測的異常都做了注釋。這里涉及到一個
SocketTimeoutException 的異常,表示連接超時異常,這個異常還是可以進行重連的,也就是說OKHTTP 內(nèi)部在連接超時時是會自動進行重連的.

協(xié)議錯誤(ProtocolException)
中斷異常(InterruptedIOException)
SSL握手錯誤(SSLHandshakeException && CertificateException)
certificate pinning錯誤(SSLPeerUnverifiedException)
4.沒用更多線路可供選擇

5..StreamAllocation

StreamAllocation是真正負責(zé)socket連接的,這里又沒有涉及到真正的socket,為什么在這里實例化?這是因為RetryAndFollowUpInterceptor 里面一些情況下是需要釋放連接的,而它又是第一個核心功能攔截器,所以必須在這里實例化,這是為了確保這里能釋放。


Anroid OKhttp筆記1 流程分析
Android OkhttpInterceptor 筆記:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 筆記:BridgeInterceptor
Android OkhttpInterceptor 筆記:ConnectInterceptor
Android OkhttpInterceptor 筆記:CacheInterceptor
Android OkhttpInterceptor 筆記:CallServerInterceptor
Android Okhttp筆記:ConnectionPool
Android Okhttp3:Dispatcher分析筆記

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 用OkHttp很久了,也看了很多人寫的源碼分析,在這里結(jié)合自己的感悟,記錄一下對OkHttp源碼理解的幾點心...
    Java小鋪閱讀 1,607評論 0 13
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請求時所使用到的各個請求庫的關(guān)系,以及 OkHttp3 的介紹。(如理解有誤,...
    小莊bb閱讀 1,329評論 0 4
  • 用OkHttp很久了,也看了很多人寫的源碼分析,在這里結(jié)合自己的感悟,記錄一下對OkHttp源碼理解的幾點心得。 ...
    藍灰_q閱讀 4,529評論 4 34
  • 上一篇我們說完了dispatcher分發(fā)器,我們知道了請求任務(wù)是如何分發(fā)出去的,那響應(yīng)是如何獲取到的呢?再看一下R...
    Thisislife閱讀 1,026評論 0 1
  • 我拉著一路忐忑的長線 從人跡罕至的起點 跑到月明星稀的秋天 我沒有看見蕭瑟的畫面 也許,風(fēng)也不曾留戀 淚眼朦朧間,...
    莫暇閱讀 251評論 8 8

友情鏈接更多精彩內(nèi)容