OkHttp3源碼詳解(三) 攔截器

首先構造一個簡單的異步網絡訪問Demo:

1.  OkHttpClient client =  new  OkHttpClient();
2.  Request request =  new  Request.Builder()
3.  .url("http://publicobject.com/helloworld.txt")
4.  .build();

6.  client.newCall(request).enqueue(new  Callback()  {
7.  @Override
8.  public  void onFailure(Call call,  IOException e)  {
9.  Log.d("OkHttp",  "Call Failed:"  + e.getMessage());
10.  }

12.  @Override
13.  public  void onResponse(Call call,  Response response)  throws  IOException  {
14.  Log.d("OkHttp",  "Call succeeded:"  + response.message());
15.  }
16.  });

2. 發起請求

OkHttpClient.newCall實際是創建一個RealCall實例:

1.  @Override
2.  public  Call newCall(Request request)  {
3.  return  new  RealCall(this, request,  false  /* for web socket */);
4.  }

RealCall.enqueue實際就是講一個RealCall放入到任務隊列中,等待合適的機會執行:

1.  @Override
2.  public  void enqueue(Callback responseCallback)  {
3.  synchronized  (this)  {
4.  if  (executed)  throw  new  IllegalStateException("Already Executed");
5.  executed =  true;
6.  }
7.  captureCallStackTrace();
8.  client.dispatcher().enqueue(new  AsyncCall(responseCallback));
9.  }

從代碼中可以看到最終RealCall被轉化成一個AsyncCall並被放入到任務隊列中,任務隊列中的分發邏輯這裏先不說,相關實現會放在OkHttp源碼分析——任務隊列疑問進行介紹。這裏只需要知道AsyncCall的excute方法最終將會被執行:

1.  [RealCall.java]
2.  @Override  protected  void execute()  {
3.  boolean signalledCallback =  false;
4.  try  {
5.  Response response = getResponseWithInterceptorChain();
6.  if  (retryAndFollowUpInterceptor.isCanceled())  {
7.  signalledCallback =  true;
8.  responseCallback.onFailure(RealCall.this,  new  IOException("Canceled"));
9.  }  else  {
10.  signalledCallback =  true;
11.  responseCallback.onResponse(RealCall.this, response);
12.  }
13.  }  catch  (IOException e)  {
14.  if  (signalledCallback)  {
15.  // Do not signal the callback twice!
16.  Platform.get().log(INFO,  "Callback failure for "  + toLoggableString(), e);
17.  }  else  {
18.  responseCallback.onFailure(RealCall.this, e);
19.  }
20.  }  finally  {
21.  client.dispatcher().finished(this);
22.  }
23.  }
24.  }

execute方法的邏輯並不複雜,簡單的說就是:

調用getResponseWithInterceptorChain獲取服務器返回
通知任務分發器(client.dispatcher)該任務已結束
getResponseWithInterceptorChain構建了一個攔截器鏈,通過依次執行該攔截器鏈中的每一個攔截器最終得到服務器返回。

3. 構建攔截器鏈
首先來看下getResponseWithInterceptorChain的實現:

源碼路徑:okhttp3/RealCall.java

 // 開始執行整個請求
 Response getResponseWithInterceptorChain() throws IOException {
   // Build a full stack of interceptors.
   // 攔截器棧
   List<Interceptor> interceptors = new ArrayList<>();
   // 前文說過的 普通攔截器
   interceptors.addAll(client.interceptors());
   // 重試攔截器,網絡錯誤、請求失敗等
   interceptors.add(retryAndFollowUpInterceptor);
   // 橋接攔截器,主要是重構請求頭即header
   interceptors.add(new BridgeInterceptor(client.cookieJar()));
   // 緩存攔截器
   interceptors.add(newCacheInterceptor(client.internalCache()));
   // 連接攔截器,連接服務器,https包裝
   interceptors.add(new ConnectInterceptor(client));
   // 網絡攔截器,websockt不支持,同樣是自定義
   if (!forWebSocket) {
     interceptors.addAll(client.networkInterceptors());
   }
   // 服務攔截器,主要是發送(write、input)、讀取(read、output)數據
   interceptors.add(new CallServerInterceptor(forWebSocket));

   // 開啓調用鏈
   Interceptor.Chain chain = new RealInterceptorChain(
       interceptors, , originalRequest);
   return chain.proceed(originalRequest);
 }

其邏輯大致分爲兩部分:

創建一系列攔截器,並將其放入一個攔截器數組中。這部分攔截器即包括用戶自定義的攔截器也包括框架內部攔截器
創建一個攔截器鏈RealInterceptorChain,並執行攔截器鏈的proceed方法
接下來看下RealInterceptorChain的實現邏輯:

 public final class RealInterceptorChain implements Interceptor.Chain {
   private final List<Interceptor> interceptors;
   private final StreamAllocation streamAllocation;
   private final HttpCodec httpCodec;
   private final RealConnection connection;
   private final int index;
   private final Request request;
   private int calls;

   public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
                               HttpCodec httpCodec, RealConnection connection, int index, Request request) {
     this.interceptors = interceptors;
     this.connection = connection;
     this.streamAllocation = streamAllocation;
     this.httpCodec = httpCodec;
     this.index = index;
     this.request = request;
   }

   @Override public Connection connection() {
     return connection;
   }

   public StreamAllocation streamAllocation() {
     return streamAllocation;
   }

   public HttpCodec httpStream() {
     return httpCodec;
   }

   @Override public Request request() {
     return request;
   }

   @Override public Response proceed(Request request) throws IOException {
     return proceed(request, streamAllocation, httpCodec, connection);
   }

   public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
       RealConnection connection) throws IOException {

     ......
     // Call the next interceptor in the chain.
     RealInterceptorChain next = new RealInterceptorChain(
         interceptors, streamAllocation, httpCodec, connection, index + , request);
     Interceptor interceptor = interceptors.get(index);
     Response response = interceptor.intercept(next);

     ...... 

     return response;
   }
 }

在proceed方法中的核心代碼可以看到,proceed實際上也做了兩件事:

創建下一個攔截鏈。傳入index + 1使得下一個攔截器鏈只能從下一個攔截器開始訪問
執行索引爲index的intercept方法,並將下一個攔截器鏈傳入該方法。
原文鏈接:https://www.bbsmax.com/A/MAzAEmQMz9/

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