1. 程式人生 > >關於Okhttp3(三)--請求流程

關於Okhttp3(三)--請求流程

前兩兩片我們介紹了,基本使用和如何檢視原始碼,今天我們正式進入原始碼分析流程。

流程圖

首先我們先看一看它的請求流程,在Okhttp3中請求是基於攔截器原理,原始碼如下:

原始碼路徑:okhttp3/RealCall.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 開始執行整個請求
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, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); }

研究Okhttp3的原始碼,從此處開始,一個一個攔截器,讀懂即可。不得不說這種方式真的很棒,清晰明瞭。

整體框架圖(圖片來源網路,感謝作者):

通過上圖,想必對Okhttp3的實現方式,已經有了基本的認識下面我們就一步一具體分析。

構造HttpClient

HttpClient採用了建造者設計模式來例項化。本身有多個欄位用於全域性物件,比去Cache、Dns等

  1. 靜態程式碼塊構造全域性快取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    static {
      Internal.instance = new Internal() {
        // 略
        @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
          // 寫入快取,指的是響應資料快取
          builder.setInternalCache(internalCache);
        }
    
        // 從快取中獲取有效的連線,僅支援Http/2, 本質就是從記憶體的ConnectiongPol中的Deque讀取
        @Override public RealConnection get(
            ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
          return pool.get(address, streamAllocation);
        }
    // 將連線快取到連線池中
        @Override public void put(ConnectionPool pool, RealConnection connection) {
          pool.put(connection);
        }
    
        // 線路的快取,多host多ip的青款
        @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
          return connectionPool.routeDatabase;
        }
    
        // 略
    }
    
      // 此處有很多種資料快取處理,不瞭解並不影響程式碼分析,如有興趣可自行研究
    
  2. Dispatcher, 分發請求,內部是有一個ThreadPoolExecutor

  3. Proxy, 代理連線,分三種類型直接(DIRECT)、Http(http)、SOCKS。

  4. ProxySelector,線路選擇器,對應Okhttp的一大特點,自行線路選擇,找到合適的連線

  5. Cache, 真正的快取實現

  6. SSLSocketFactory, Https的支援

  7. ConnectionPool, 連線池

  8. Dns,dns解析(Java實現)

  9. 其他,如超時時間等

發起請求

我們都知道一個請求有多部分組成,同樣Okhttp3建立一個請求也要多部分。

  1. 構造請求頭
  2. 構造請求體
  3. 傳送一個請求

通過以上三步我們就可以完成一次請求。

請求頭

header並非每個請求都需要,要看與服務端是如何定義的,通常一個請求會預設一些頭,比如Content-Type,Accept-Encoding,Connection等對應http協議

Header本質上就是一個Map,只是在封裝了一層而已,但是Okhttp3的實現不是這樣,而是一個String資料,key + value的形式,即一個頭佔用陣列的兩位:

每一個色塊對應一個header,程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
 // 原始碼路徑 okhttp3/Headers.java

public final class Headers {
  private final String[] namesAndValues;
  // 略
  Builder addLenient(String name, String value) {
    namesAndValues.add(name);
    namesAndValues.add(value.trim());
    return this;
  }
  // 略
 }
請求體

請求體有多種形式,對應的父類是RequestBody,有檔案形式、Json等,MediaType決定了是何種形式,通常我們用的是FromBody和MultipartBody

  1. FromBody

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    public final class FormBody extends RequestBody {
      private static final MediaType CONTENT_TYPE =
          MediaType.parse("application/x-www-form-urlencoded");
    
      private final List<String> encodedNames; // 引數名稱
      private final List<String> encodedValues; // 引數值
    
     // 構造者
       public static final class Builder {
        private final List<String> names = new ArrayList<>();
        private final List<String> values = new ArrayList<>();
    
        public Builder add(String name, String value) {
          names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true));
          values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true));
          return this;
        }
    
         //構造一個例項
        public FormBody build() {
          return new FormBody(names, values);
        }
      }
     }
    
  2. MultipartBody

    MultipartBody原理基本一致,區別在於他可以傳送表單的同時也可以傳送檔案資料,再次不在贅述。

構造一個Request

有了上面兩個步驟,接下了就自然而讓產生一個Request,顧名思義它就是對請求的封裝,包括請求方式,請求頭,請求體,請求路徑等,原始碼碼也是比較簡單,一看即明白。

1
2
3
4
5
6
7
public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final RequestBody body;
  final Object tag;
 }
Call

有了準備工作,現在我們就要發射了,Call是一個概念的封裝,就像一列火車,蓄勢待發,在鐵軌停靠準備出發一樣

Call是一個介面,實現類只有一個RealCall,上面我們提到的流程就是在RealCall中。

1
2
3
4
5
6
7
8
9
10
11
12
public interface Call extends Cloneable {
  Request request(); // 獲取請求封裝的資料
  Response execute() throws IOException; // 同步執行
  void enqueue(Callback responseCallback); // 非同步執行
  void cancel(); // 取消請求
  boolean isExecuted();
  boolean isCanceled();
  Call clone();
  interface Factory {
    Call newCall(Request request);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 重點看看中文註釋處即可
final class RealCall implements Call {

  final OkHttpClient client;
  // 重試攔截器
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

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

  // 同步執行的實現
  @Override public Response execute() throws IOException {
    // 一個call只能執行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      // 將請求放在,請求池中執行,此處會立即執行
      client.dispatcher().executed(this);
      // 獲取結果,即執行多個連結器的呼叫鏈
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

  private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

  //  非同步執行,不管行返回結果
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // AsyncCall是一個Runnable的實現類,同時一個是RealCall的內部類
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

  @Override public void cancel() {
    retryAndFollowUpInterceptor.cancel();
  }

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

  @Override public boolean isCanceled() {
    return retryAndFollowUpInterceptor.isCanceled();
  }

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public RealCall clone() {
    return new RealCall(client, originalRequest, forWebSocket);
  }

  StreamAllocation streamAllocation() {
    return retryAndFollowUpInterceptor.streamAllocation();
  }

  // 非同步執行的執行緒封裝,Android基本就是這裡了
  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        // 執行呼叫鏈,是不是很重要,哈哈
        Response response = getResponseWithInterceptorChain();
        // 處理回掉
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

  /**
   * Returns a string that describes this call. Doesn't include a full URL as that might contain
   * sensitive information.
   */
  String toLoggableString() {
    return (isCanceled() ? "canceled " : "")
        + (forWebSocket ? "web socket" : "call")
        + " to " + redactedUrl();
  }

  String redactedUrl() {
    return originalRequest.url().redact();
  }
	// 最重要的入口了
  	// 最重要的入口了
  	// 最重要的入口了
  // 重要事情說三遍
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 脊髓程式碼開始處
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
}

看來上面的程式碼,是不是被人家的想法折服。更精妙的還在後面呢,不要急。到此,一個請求已經完成,傳送和接收,(不關心內部實現的話)。接下來我們再來看看,聯結器是如何工作的。

攔截器工作原理

Interceptor是一個介面,主要是對請求和響應的處理,而實現攔截器呼叫鏈的是其內部介面Chain

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Interceptor {
// 管攔截
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    // 管分發,前行
    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

Interceptor.Chain的實現類只有一個RealInterceptorChain,也是處理呼叫過程的實現,其內部有個List裝載了所有攔截器,大家想必也猜到了,對,就是迭代,當然也不是簡簡單單的接待了事。讓我們看看原始碼實現。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35