一、介紹
OKHttp是一款高效的HTTP客戶端,支持連接同一地址的鏈接共享同一個socket,通過連接池來減小響應延遲,還有透明的GZIP壓縮,請求緩存等優(yōu)勢,其核心主要有路由、連接協(xié)議、攔截器、代理、安全性認證、連接池以及網(wǎng)絡適配,攔截器主要是指添加,移除或者轉(zhuǎn)換請求或者回應的頭部信息(新版4.x.x是使用kotlin寫的,所以我們這里分析的是3.14.x的版本)
二、使用
OkHttpClient okHttpClient=new OkHttpClient();
final Request request=new Request.Builder()
.url("https://www.wanandroid.com/navi/json")
.get()
.build();
final Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
Log.e("同步結(jié)果---- ",response.body().string()+"");
} catch (IOException e) {
e.printStackTrace();
}
- 構(gòu)造OKHttpClient
- 構(gòu)造Request
- 得到一個Call
- 同步或異步執(zhí)行Call,對應execute()和enqueue方法(線程池最終也是調(diào)用execute())
這里有個任務分發(fā)器Dispatcher
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
//線程池對象
private @Nullable ExecutorService executorService;
//準備執(zhí)行的線程
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的異步線程
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的同步線程
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
這個來用來操作控制任務請求,使用Deque雙端隊列操作
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
方法的重點就是返回的Response result = getResponseWithInterceptorChain()
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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
這里是okhttp的精髓Interceptor,使用了責任鏈模式,類似裝飾著模式,層層封裝,給request和Response做不同的處理,View的點擊事件使用的就是責任鏈模式,可以攔截和消費事件。當然我們可以添加自己的Interceptor。
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 + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//去掉異常處理
return response;
}
Interceptor的順序分別是自定義Interceptor,RetryAndFollowUpInterceptor請求重定向攔截器,重定向攔截器初始化了StreamAllocation對象,這個對象包含請求的一系列參數(shù),每個請求都會有一個StreamAllocation對象,但他們共用Call對象和Address對象,Address包含url,dns,socket和proxy等處理對象,也就是說,RetryAndFollowUpInterceptor中初始化了一系列請求初始化的參數(shù),然后調(diào)用下一個攔截器
BridgeInterceptor
在BridgeInterceptor中拼接了請求頭信息,比如支持gzip格式的參數(shù)
對返回的response做處理
//響應header, 如果沒有自定義配置cookieJar==null,則什么都不做
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//判斷服務器是否支持gzip壓縮格式,如果支持則交給kio壓縮
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);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
CacheInterceptor
http緩存分為兩種,一種強制緩存,一種對比緩存,強制緩存生效時直接使用以前的請求結(jié)果,無需發(fā)起網(wǎng)絡請求。對比緩存生效時,無論怎樣都會發(fā)起網(wǎng)絡請求,如果請求結(jié)果未改變,服務端會返回304,但不會返回數(shù)據(jù),數(shù)據(jù)從緩存中取,如果改變了會返回數(shù)據(jù)。
//如果緩存和返回都是空的,直接返回504錯誤
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 如果強制讀緩存,就去緩存讀取
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//調(diào)用下一個Interceptor
networkResponse = chain.proceed(networkRequest);
} finally {
// 報錯回收資源
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
if (cacheResponse != null) {
// 如果返回304,直接調(diào)用緩存數(shù)據(jù)
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}