OKHTTP異步和同步請求簡單分析
OKHTTP攔截器緩存策略CacheInterceptor的簡單分析
OKHTTP攔截器ConnectInterceptor的簡單分析
OKHTTP攔截器CallServerInterceptor的簡單分析
OKHTTP攔截器BridgeInterceptor的簡單分析
OKHTTP攔截器RetryAndFollowUpInterceptor的簡單分析
OKHTTP結(jié)合官網(wǎng)示例分析兩種自定義攔截器的區(qū)別
BridgeInterceptor
/**
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/
public final class BridgeInterceptor implements Interceptor {
private final CookieJar cookieJar;
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
//對請求頭的補充
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
//默認是保持連接的 Keep-Alive
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
//默認是 GZIP 壓縮的
//Accept-Encoding : 就是告訴服務(wù)器客戶端能夠接受的數(shù)據(jù)編碼類型植袍,OKHTTP 默認就是 GZIP 類型肛宋。
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null) {
//標(biāo)記請求支持 GZIP 壓縮
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//cookie 頭的添加
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
//user-egent "okhttp/3.4.1"
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
//發(fā)送網(wǎng)絡(luò)請求
Response networkResponse = chain.proceed(requestBuilder.build());
//接受服務(wù)器返回的 Cookie
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
//當(dāng)服務(wù)器返回的數(shù)據(jù)是 GZIP 壓縮的堰塌,那么客戶端就有責(zé)任去進行解壓操作
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
//構(gòu)建一個Response
return responseBuilder.build();
}
/** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
private String cookieHeader(List<Cookie> cookies) {
StringBuilder cookieHeader = new StringBuilder();
for (int i = 0, size = cookies.size(); i < size; i++) {
if (i > 0) {
cookieHeader.append("; ");
}
Cookie cookie = cookies.get(i);
cookieHeader.append(cookie.name()).append('=').append(cookie.value());
}
return cookieHeader.toString();
}
}
BridgeInterceptor 攔截器的功能主要有以下 3 點:
是負責(zé)將用戶構(gòu)建的一個 Request 請求轉(zhuǎn)化為能夠進行網(wǎng)絡(luò)訪問的請求。
將這個符合網(wǎng)絡(luò)請求的 Request 進行網(wǎng)絡(luò)請求。
將網(wǎng)絡(luò)請求回來的響應(yīng) Response 轉(zhuǎn)化為用戶可用的 Response态罪。
下面會通過源碼的角度來分析這三點是如何實現(xiàn)的?
1炉峰、用戶 Request 到 網(wǎng)絡(luò) Request 的轉(zhuǎn)換
因為用戶在構(gòu)建一個 Request 對象的時侯往往只會簡單設(shè)置 url,RequestBody 等幾個屬性,其實這樣就已經(jīng)可以發(fā)送一個網(wǎng)絡(luò)請求了疼阔,但是真正要發(fā)送一個請求實際上沒這么簡單,在 OKHTTP 這么優(yōu)秀的框架的光環(huán)下婆廊,使調(diào)用者使用這個框架去發(fā)送一個網(wǎng)絡(luò)請求是非常簡單的迅细,因為很多功能它內(nèi)部都進行了高度的封裝。
如何對用戶 Request 到 網(wǎng)絡(luò) Request 的轉(zhuǎn)換呢茵典?從上面的代碼中可以看出它在原來 request
的基礎(chǔ)上添加了很多請求頭,下面列舉的是添加的頭信息统阿。
Content-Type 定義網(wǎng)絡(luò)文件的類型和網(wǎng)頁的編碼枚尼,如果未指定 ContentType,默認為[TEXT]/[HTML]砂吞,具體的類型署恍,參考這個文檔 HTTP Content-type
Content-Length 表示的是請求體內(nèi)容的長度。它和 Transfer-Encoding 是互斥的盯质,主要根據(jù) Content-Length 是否為 -1 進行判讀使用哪一個請求頭。
Transfer-Encoding 值為 chunked 表示請求體的內(nèi)容大小是未知的呼巷。
Host 請求的 url 的主機
Connection 默認就是 "Keep-Alive"赎瑰,就是一個 TCP 連接之后不會關(guān)閉王悍,保持連接狀態(tài)餐曼。
Accept-Encoding 默認是 "gzip" 告訴服務(wù)器客戶端支持 gzip 編碼的響應(yīng)。
Cookie 當(dāng)請求設(shè)置了 Cookie 那么就是添加 Cookie 這個請求頭源譬。
User-Agent "okhttp/3.4.1" 這個值根據(jù) OKHTTP 的版本不一樣而不一樣,它表示客戶端 的信息踩娘。
上面就是將一個普通的 Request 添加很多頭信息,讓其成為可以發(fā)送網(wǎng)絡(luò)請求的 Request 雷绢。
2理卑、將這個 Request 進行網(wǎng)絡(luò)請求得到響應(yīng) Response
這個過程涉及到其他幾個攔截器的操作翘紊,具體是怎么進行網(wǎng)絡(luò)請求傻工,我們暫時不要管,具體查閱我先前寫幾篇 BLOG 中捆。我們只管這一步操作是一個向服務(wù)發(fā)送請求的操作坊饶,并且這個請求之后服務(wù)端會給客戶端返回一個 Response 響應(yīng)就行了。
Response networkResponse = chain.proceed(requestBuilder.build());
3匿级、將網(wǎng)絡(luò)請求回來的響應(yīng) Response 轉(zhuǎn)化為用戶可用的 Response染厅。
這句話怎么理解呢津函?例如 OKHTTP 默認就是支持 GZIP 壓縮的肖粮,若服務(wù)器返回的響應(yīng)體是經(jīng)過 GZIP 壓縮的,那么 BridgeInterceptor 就有責(zé)任將其進行解壓尔苦,那么調(diào)用者無需關(guān)系這個過程了涩馆,只要得到最終的 Response 即可允坚。
3.1、獲取服務(wù)器返回的 Cookie
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
3.2稠项、將網(wǎng)絡(luò)請求返回的響應(yīng)進行轉(zhuǎn)化
當(dāng) transparentGzip 為 true ,表示請求設(shè)置的 Accept-Encoding 是 支持gzip 壓縮的展运,意思就是告知服務(wù)器客戶端是支持 gzip 壓縮的,然后再判斷服務(wù)器的響應(yīng)頭 Content-Encoding 是否也是 GZIP 壓縮的划乖,意思就是響應(yīng)體內(nèi)容是否是經(jīng)過 GZIP 壓縮的挤土,如果都成立的條件下,那么它會將 Resposonse.body().source() 的輸入流 BufferedSource 轉(zhuǎn)化為 GzipSource 類型仰美,這樣的目的就是讓調(diào)用者在使用 Response.body().string() 獲取響應(yīng)內(nèi)容時就是以解壓的方式進行讀取流數(shù)據(jù)。
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
4庆寺、總結(jié)
BidgeInterceptor 這個攔截器的功能相對簡單一些诉字,主要就是上面提及到 3 點內(nèi)容懦尝。這里設(shè)計的一個 Cookie 的操作壤圃,本文并沒有詳細分析實現(xiàn),以后再看看吧踊挠。