天天看點

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/

繼續閱讀