半年前閱讀了Volley源碼,可是如今主流網絡請求都是使用OkHttp + Retrofit + RxJava,所以打算好好研究下OkHttp的源碼(基於OkHttp3.14.1),記錄一下java
這裏只例舉基本的Get請求,詳細的請看官方文檔git
val client = OkHttpClient()
fun syncGet(url: String) {
val request = Request.Builder()
.url(url)
.build()
val call = client.newCall(request)
val response = call.execute()
if (response.isSuccessful) {
Log.d(TAG, "請求成功: " + response.body()!!.string())
} else {
Log.d(TAG, "請求失敗: " + response.message())
}
}
fun asyncGet(url: String) {
val request = Request.Builder()
.url(url)
.build()
val call = client.newCall(request)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "錯誤: " + e.message)
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
Log.d(TAG, "請求成功: " + response.body()!!.string())
} else {
Log.d(TAG, "請求失敗: " + response.message())
}
}
})
}
複製代碼
由上可知,發送一個基本的get請求須要以下幾步github
下面按照這五步探索下源碼web
首先看看OkHttpClient的構造器設計模式
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
// 主要是從builder中取出對應字段進行賦值,忽略
...
}
複製代碼
其擁有兩個構造器不過咱們只能直接調用無參的那個,另外一個主要給Builder的build方法使用的(典型的建造者模式),接着看看Builder的構造器數組
public Builder() {
// 建立異步執行策略
dispatcher = new Dispatcher();
// 默認協議列表Http1.一、Http2.0
protocols = DEFAULT_PROTOCOLS;
// 鏈接規格,包括TLS(用於https)、CLEARTEXT(未加密用於http)
connectionSpecs = DEFAULT_CONNECTION_SPECS;
// 事件監聽,默認沒有
eventListenerFactory = EventListener.factory(EventListener.NONE);
// 代理選擇器
proxySelector = ProxySelector.getDefault();
// 使用空對象設計模式
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
// 提供Cookie策略的持久性
cookieJar = CookieJar.NO_COOKIES;
// socket工廠
socketFactory = SocketFactory.getDefault();
// hostname驗證器
hostnameVerifier = OkHostnameVerifier.INSTANCE;
// 證書標籤
certificatePinner = CertificatePinner.DEFAULT;
// 代理認證
proxyAuthenticator = Authenticator.NONE;
// 認證
authenticator = Authenticator.NONE;
// 鏈接池
connectionPool = new ConnectionPool();
// dns
dns = Dns.SYSTEM;
// 跟隨ssl重定向
followSslRedirects = true;
// 跟隨重定向
followRedirects = true;
// 當鏈接失敗時嘗試
retryOnConnectionFailure = true;
callTimeout = 0;
// 鏈接、讀取、寫超時10秒
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
Builder(OkHttpClient okHttpClient) {
// 從已有的OkHttpClient實例中取出對應的參數賦值給當前實例
...
}
複製代碼
Builder中的屬性到用到時再好好的研究,接着來看看第二步Request實例的建立緩存
Request實例經過Builder進行建立的,所以首先看看Request.Builder的構造器服務器
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);
this.headers = request.headers.newBuilder();
}
複製代碼
其提供了兩個構造器,不過咱們只能調用無參的那個其內部設置了默認請求方法爲GET,而且建立了一個HeaderBuilder實例用於統一管理請求頭,第二個構造器用於Request實例中拷貝出對應參數賦值給當前Builder實例,接着看看其url方法和build方法cookie
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// 默默的將web socket urls替換成http urls
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
複製代碼
其中url方法主要是將請求地址封裝成HttpUrl實例並賦值給成員url,build方法建立了Request實例。至此第二步結束了接着看看第三步Call實例的建立網絡
經過調用OkHttpClient實例的newCall()建立Call實例
// OkHttpClient.java
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall.java
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
// Transmitter.java
public Transmitter(OkHttpClient client, Call call) {
this.client = client;
this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
this.call = call;
this.eventListener = client.eventListenerFactory().create(call);
// 設置了AsyncTimeout的超時時間默認是0,下文execute方法執行時會用到
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
複製代碼
newCall方法返回了一個RealCall實例,而且初始化了其transmitter屬性(暫時不用管Transmitter的做用),而在Transmitter構造器中又初始化了connectionPool、eventListener屬性,其中Internal.instance在OkHttpClient這個類一加載時就初始化了
// OkHttpClient.java
Internal.instance = new Internal() {
...
@Override
public RealConnectionPool realConnectionPool(ConnectionPool connectionPool) {
return connectionPool.delegate;
}
...
}
複製代碼
接着主要看看client.connectionPool方法和eventListenerFactory方法
// OkHttpClient.java
public Builder() {
connectionPool = new ConnectionPool();
}
public final class ConnectionPool {
final RealConnectionPool delegate;
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.delegate = new RealConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit);
}
// 返回當前鏈接池的空閒鏈接數
public int idleConnectionCount() {
return delegate.idleConnectionCount();
}
// 返回當前鏈接池的總共鏈接數
public int connectionCount() {
return delegate.connectionCount();
}
// 關閉移除當前鏈接池中全部空閒的鏈接
public void evictAll() {
delegate.evictAll();
}
}
// RealConnectionPool.java
public RealConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
public ConnectionPool connectionPool() {
return connectionPool;
}
public Builder() {
eventListenerFactory = EventListener.factory(EventListener.NONE);
}
static EventListener.Factory factory(EventListener listener) {
return call -> listener;
}
public EventListener.Factory eventListenerFactory() {
return eventListenerFactory;
}
複製代碼
從上源碼能夠得出其實connectionPool最終實現是RealConnectionPool、eventListener默認是EventListener.NONE,至此RealCall實例也已經建立完畢了,接着進入最重要的一步execute或者enqueue,咱們先看看同步的狀況
咱們知道call實際上是一個RealCall實例,所以看看其execute方法
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}
// Transmitter.java
public final class Transmitter {
public Transmitter(OkHttpClient client, Call call) {
this.client = client;
this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
this.call = call;
this.eventListener = client.eventListenerFactory().create(call);
// 設置了AsyncTimeout的超時時間默認是0
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
// AsyncTimeout具體實現就不深究了,只需知道當調用了enter方法後若是達到了設置的時間還沒調用
// 就會調用timedOut方法,而這裏由於默認的callTimeout是0,所以不會超時
private final AsyncTimeout timeout = new AsyncTimeout() {
@Override
protected void timedOut() {
cancel();
}
};
public void timeoutEnter() {
timeout.enter();
}
public void callStart() {
// 建立了一個Throwable記錄了當前棧信息
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
// 回調callStart,默認設置的沒處理任何事件
eventListener.callStart(call);
}
}
// Dispatcher.java
public final class Dispatcher {
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
}
複製代碼
首先會判斷一下當前call是否已經被執行過,若是已經被執行過了就會拋出異常,而後開啓WatchDog監聽Timeout,若是超時就會取消本次請求,接着記錄當前的棧信息並回調callStart,而後將當前RealCall實例加入到OkHttpClient實例內dispatcher的一個雙端隊列中去,接着執行getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
複製代碼
該方法首先將OkHttpClient的全部Interceptor與框架本身的幾個Interceptor進行組合,而後建立RealInterceptorChain實例調用其proceed方法獲取到Response,所以網絡請求的主要邏輯就是在這個proceed方法中
// RealInterceptorChain
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
// 默認index = 0,若是index超出了攔截器的總長就拋出錯誤
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 這裏剛纔傳入的exchange爲null
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// 同上
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 又建立了一個RealInterceptorChain實例,不過其index在原來基礎上加了1
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
// 取出一個Interceptor實例調用其intercept方法
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// 若是Interceptor返回了null那麼拋出NPE
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
複製代碼
該方法內部主要從interceptors中取出index位置上的一個Interceptor實例,而後建立一個index=index+1的RealInterceptorChain實例next,最後調用Interceptor實例的interceptor方法將next傳入,獲取到Response返回,那麼網絡請求重點其實在interceptor.intercept方法內,而默認index等於0,咱們的OkHttpClient本身又沒有設置Interceptor,因而會調用到RetryAndFollowUpInterceptor(負責失敗重試以及重定向)實例的intercept方法
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
response = realChain.proceed(request, transmitter, null);
// 表示請求成功可是多是一個重定向響應
success = true;
} catch (RouteException e) {
// 嘗試經過路由鏈接失敗請求將不會發送,判斷是否須要進行重試
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// 試圖與服務器通訊失敗,請求可能已經發送,判斷是否須要進行重試
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// 若是請求沒有成功則釋放資源
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// 若是上個響應存在(表示上個響應是一個重定向響應,其不會擁有響應體),構建一個新的Response實例
// 將上個響應賦值給priorResponse屬性
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
// 暫時不理解這個exchange是幹什麼用的??
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
// 根據響應頭判斷是不是重定向,若是是就會新建一個Request實例返回
Request followUp = followUpRequest(response, route);
// 若是followUp爲空也就是沒有重定向那麼直接返回響應
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
// 若是限制只發送一次,那也直接返回響應
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
// 重定向請求太多了,就拋出異常
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 賦值新建的請求,保存上一個響應,最終的響應會包含全部前面重定向的響應
request = followUp;
priorResponse = response;
}
}
複製代碼
RetryAndFollowUpInterceptor主要作的就是重試以及處理重定向,內部會調用realChain.proceed調用下層Interceptor實例的intercept方法,當下層Interceptor拋出異常會判斷是否有重試的必要 ,當下層返回了一個Response,其會根據該Response判斷是否爲重定向響應,若是是就會建立新建一個Request實例,再次請求獲取到新的Response實例後將原先的Response賦值給其priorResponse屬性,以此循環直到請求成功(再也不重定向)、超出最大重定向數、拋出不可重試的異常。而後看看BridgeInterceptor
public final class BridgeInterceptor implements Interceptor {
private final CookieJar cookieJar;
// 這裏的CookieJar就是OkHttpClient的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();
// 若是有請求體,而且請求頭若是沒有Content-Type、Content-length、Host、Connection就加上
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));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// 而後若是沒有Accept-Encoding請求頭而且不是請求部分資源,那麼加上Accept-Encoding: gzip
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
// 從cookieJar中取出Cookie列表
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
// 沒有UserAgrent就添加爲okhttp/3.14.1
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 調用下一個Interceptor
Response networkResponse = chain.proceed(requestBuilder.build());
// 使用cookieJar保存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)) {
// 設置對應的響應頭
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");
// 自動進行解壓,不過contentLength變成了-1
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
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的邏輯很清晰,其作爲應用程序代碼與網絡代碼中間的橋樑,首先根據給用戶的Request實例建立一個添加了某些請求頭的Request實例;其次調用了chain.proceed執行下一個攔截器;最後將下層返回的Response實例構建成用戶須要的Response實例,此外上述代碼還告訴咱們若是想要持久化管理cookie能夠實現CookieJar這個接口設置給OkHttpClient,接着繼續看看下一層CacheInterceptor
public final class CacheInterceptor implements Interceptor {
// 這個cache就是OkHttpClient裏面的internalCache
final InternalCache cache;
public CacheInterceptor(@Nullable InternalCache cache) {
this.cache = cache;
}
@Override
public Response intercept(Chain chain) throws IOException {
// 若是給OkHttpClient設置了InternalCache,那麼從裏面獲取緩存的響應
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 這個裏面主要是根據請求和緩存的響應判斷緩存是否命中
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body());
}
// 客戶端設置了only-if-cached,表示只使用緩存而緩存又沒有命中所以直接構建一個Response返回
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();
}
// 緩存命中,構造一個Response實例並將去掉了body的cacheResponse賦值給該實例的cacheResponse屬性
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// 發生了異常須要將緩存響應體關閉
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// 若是有緩存響應而且響應碼是304,就根據返回的響應和緩存的響應構造一個新的響應而且更新下緩存
if (cacheResponse != null) {
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();
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
// 響應碼不是304,則構造一個新的Response將cacheResponse、networkResponse分別去掉body賦值給
// cacheResponse和networkResponse
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 將最終響應放到cache中
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
}
}
}
return response;
}
}
public final class CacheStrategy {
public static class Factory {
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;
// 根據緩存響應頭提取出一些緩存有關的信息
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
Headers headers = cacheResponse.headers();
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
}
}
public CacheStrategy get() {
CacheStrategy candidate = getCandidate();
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
// 若是設置了onlyIfCached那麼在CacheInterceptor的intercept方法內部會直接構造一個響應碼爲504的Response
return new CacheStrategy(null, null);
}
return candidate;
}
private CacheStrategy getCandidate() {
// 沒有緩存響應就直接建立一個沒響應的CacheStrategy實例
if (cacheResponse == null) {
return new CacheStrategy(request, null);
}
// 丟棄緩存響應,若是請求是https而且缺乏必要的握手
if (request.isHttps() && cacheResponse.handshake() == null) {
return new CacheStrategy(request, null);
}
// 若是不該該使用緩存也丟棄緩存響應
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}
CacheControl requestCaching = request.cacheControl();
// 若是Request包含noCache請求頭,或者帶上了 If-Modified-Since、If-None-Match兩個請求頭也丟棄響應
// 應該帶上這兩個請求頭表示客戶端在循環服務端資源是否發生變化,沒變化會返回304,所以不該該使用緩存
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}
CacheControl responseCaching = cacheResponse.cacheControl();
long ageMillis = cacheResponseAge();
long freshMillis = computeFreshnessLifetime();
if (requestCaching.maxAgeSeconds() != -1) {
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}
long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) {
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}
long maxStaleMillis = 0;
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
}
// 根據時間判斷是否緩存命中,不知道具體是怎麼判斷的?
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
// 添加If-None-Match、If-Modified-Since請求頭
String conditionName;
String conditionValue;
if (etag != null) {
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
conditionName = "If-Modified-Since";
conditionValue = lastModifiedString;
} else if (servedDate != null) {
conditionName = "If-Modified-Since";
conditionValue = servedDateString;
} else {
return new CacheStrategy(request, null);
}
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
return new CacheStrategy(conditionalRequest, cacheResponse);
}
複製代碼
CacheInterceptor的主要功能就是從InternalCache中取出保存的響應,而後根據請求和緩存的響應判斷緩存是否命中,命中就會直接構建一個新的響應返回,若是沒命中(因爲響應過時),則會根據緩存響應的ETag、LastModify等響應頭去構造當前的請求頭,這樣當服務器判斷資源沒變化時能夠直接返回304,框架也只須要更新下緩存的響應頭就能夠直接返回了。OkHttp爲咱們提供了一個Cache類(內部使用DiskLruCache實現)若是咱們須要可以緩存只須要進行以下設置.
val client = OkHttpClient.Builder().cache(Cache(cacheFile, 50 * 1000)).build()
複製代碼
接着看看下一個攔截器ConnectInterceptor
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
return realChain.proceed(request, transmitter, exchange);
}
}
複製代碼
ConnectIntercept代碼不多,可是作的事情不少,其會先去ConnectionPoll中尋找是否有合適的RealConnection,若是沒有找到會去請求dns服務器獲取目標IP再將目標IP封裝成一個Route實例而後建立一個RealConnection接着建立Socket實例併發起鏈接若是是Https請求還會發起握手,校驗證書,接着構建Http1ExchangeCodec實例,最後再去構建Exchange實例,接着再看看CallServerInterceptor
public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;
public CallServerInterceptor(boolean forWebSocket) {
this.forWebSocket = forWebSocket;
}
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
// 根據Request生成對應的字節數組而且寫入到Buffer中
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
exchange.writeRequestHeaders(request);
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
// 若是請求包含請求體,寫入請求體
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
responseBuilder = exchange.readResponseHeaders(true);
}
if (responseBuilder == null) {
if (request.body().isDuplex()) {
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
} else {
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
exchange.noRequestBody();
if (!exchange.connection().isMultiplexed()) {
exchange.noNewExchangesOnConnection();
}
}
} else {
exchange.noRequestBody();
}
if (request.body() == null || !request.body().isDuplex()) {
// 將Buffer中的數據寫給服務端
exchange.finishRequest();
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
if (responseBuilder == null) {
// 獲取響應頭
responseBuilder = exchange.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
exchange.responseHeadersEnd(response);
if (forWebSocket && code == 101) {
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
複製代碼
CallServerInterceptor真正的進行了網絡請求,會根據Request實例構建出Http請求,獲取到Http響應後再構建出HttpResponse,網絡請求成功後會接着執行前幾個Interceptor的剩餘代碼,這裏就不看了。直接回到RealCall.execute()
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
複製代碼
能夠看出當一次同步請求結束後,會將RealCall中隊列中移除,而後啓動正在等待的異步請求,若是沒有異步請求會回調IdleCallback。接着看看異步請求過程
enqueue方法用於執行異步請求
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) {
throw new IllegalStateException("Already Executed");
}
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製代碼
這裏都和execute同樣只是最後調用了Dispatcher的enqueue方法,不過傳入的是AsyncCall實例
//Dispatcher.java
void enqueue(AsyncCall call) {
synchronized (this) {
// 將call加入到隊列中去
readyAsyncCalls.add(call);
if (!call.get().forWebSocket) {
// 剛剛建立的call不是使用webSocket因此進入這裏
AsyncCall existingCall = findExistingCallWithHost(call.host());
// 目的只是爲了統計每一個Host有幾個AsyncCall
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
// 從正在執行或者等待執行的call隊列中取出host屬性爲host的AsyncCall實例
private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 若是已經達到最大請求數64就中止執行
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
// 若是每一個Host達到了最大請求數5個就跳過該call
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
// 每一個端口請求計數加1
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
// 建立一個線程池,而後執行AsyncCall的execute方法
asyncCall.executeOn(executorService());
}
return isRunning;
}
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
AtomicInteger callsPerHost() {
return callsPerHost;
}
void reuseCallsPerHostFrom(AsyncCall other) {
this.callsPerHost = other.callsPerHost;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
@Override
protected void execute() {
// 在線程池中執行
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
if (signalledCallback) {
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// 每一個端口請求計數減1
client.dispatcher().finished(this);
}
}
}
複製代碼
若是調用了enqueue發送網絡請求,那麼最終會在線程池中執行AsyncCall的execute方法,其內部實現與同步執行基本相似,注意最後會在子線程中直接調用onResponse,所以咱們不能在onResponse裏面直接更新UI。咱們能夠寫一個WrapCall將Call進行包裝這樣就能實現回調在主線程了,代碼以下
class WrapCall(private val call: Call) : Call by call {
override fun enqueue(responseCallback: Callback) {
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
handler.post {
responseCallback.onFailure(call, e)
}
}
override fun onResponse(call: Call, response: Response) {
handler.post {
responseCallback.onResponse(call, response)
}
}
})
}
companion object {
private val handler = Handler(Looper.getMainLooper())
}
}
// 外界使用,只要包裝下
val call = WrapCall(client.newCall(request))
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "錯誤: " + e.message)
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
Log.d(TAG, "請求成功: ${Looper.myLooper() === Looper.getMainLooper()}" + response.body()!!.string())
} else {
Log.d(TAG, "請求失敗: ${Looper.myLooper() === Looper.getMainLooper()}" + response.message())
}
}
})
複製代碼
源碼分析到這網絡流程基本已經清晰,下面再來看看OkHttp的鏈接複用。
鏈接池的具體實現是RealConnectionPool,每次在ConnectInterceptor的intercepte方法都會嘗試着先從鏈接池中取出一個鏈接,取不到知足條件的纔會新建一個鏈接
public final class RealConnectionPool {
// 這個線程池是專門用來執行清理線程的
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp ConnectionPool", true));
// 最大空閒鏈接數 默認5個
private final int maxIdleConnections;
// 最大保存存活的空閒鏈接時間 默認5分鐘
private final long keepAliveDurationNs;
// 當向鏈接池中加入一個鏈接後會執行清理操做,內部會尋找空閒時間最長的鏈接,若是其空閒時間
// 已經超過了最長時間就會將其關閉,否則就等待指定時間
private final Runnable cleanupRunnable = () -> {
while (true) {
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (RealConnectionPool.this) {
try {
RealConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
}
}
}
}
};
// 雙端隊列保存當前OkHttpClient的全部鏈接
private final Deque<RealConnection> connections = new ArrayDeque<>();
// 記錄了失敗的路由
final RouteDatabase routeDatabase = new RouteDatabase();
// 清理線程是否正在執行
boolean cleanupRunning;
// 首先建立了一個最大空閒鏈接爲5,最大保存存活時間5分鐘的鏈接池
public RealConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
// 獲取空閒鏈接數量
public synchronized int idleConnectionCount() {
int total = 0;
for (RealConnection connection : connections) {
if (connection.transmitters.isEmpty()) total++;
}
return total;
}
// 總共的鏈接數
public synchronized int connectionCount() {
return connections.size();
}
// Transmitter調用該方法獲取鏈接,內部判斷若是鏈接池有鏈接知足條件就返回true,否則返回false
boolean transmitterAcquirePooledConnection(Address address, Transmitter transmitter, @Nullable List<Route> routes, boolean requireMultiplexed) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (requireMultiplexed && !connection.isMultiplexed()) continue;
if (!connection.isEligible(address, routes)) continue;
transmitter.acquireConnectionNoEvents(connection);
return true;
}
return false;
}
// 將一個新建的鏈接放入到鏈接池中
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
// 若是清理線程還沒運行就開始運行
if (!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunnable);
}
// 再將鏈接放入雙端隊列中去
connections.add(connection);
}
// 當一個鏈接從執行中變成了空閒時調用,該方法會喚醒清理現場
boolean connectionBecameIdle(RealConnection connection) {
assert (Thread.holdsLock(this));
if (connection.noNewExchanges || maxIdleConnections == 0) {
connections.remove(connection);
return true;
} else {
notifyAll(); // Awake the cleanup thread: we may have exceeded the idle connection limit.
return false;
}
}
// 關閉全部的鏈接
public void evictAll() {
List<RealConnection> evictedConnections = new ArrayList<>();
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
if (connection.transmitters.isEmpty()) {
connection.noNewExchanges = true;
evictedConnections.add(connection);
i.remove();
}
}
}
for (RealConnection connection : evictedConnections) {
closeQuietly(connection.socket());
}
}
// 若是能夠清理的話就關閉Socket返回0,否則返回須要等待的時間
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
idleConnectionCount++;
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
return keepAliveDurationNs;
} else {
cleanupRunning = false;
return -1;
}
}
closeQuietly(longestIdleConnection.socket());
return 0;
}
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
// 對於Http1.1這個List最多也只有一個元素
List<Reference<Transmitter>> references = connection.transmitters;
for (int i = 0; i < references.size(); ) {
Reference<Transmitter> reference = references.get(i);
if (reference.get() != null) {
i++;
continue;
}
// 發現一個泄露的transmitter,這是一個應用程序的bug
TransmitterReference transmitterRef = (TransmitterReference) reference;
String message = "A connection to " + connection.route().address().url()
+ " was leaked. Did you forget to close a response body?";
Platform.get().logCloseableLeak(message, transmitterRef.callStackTrace);
references.remove(i);
connection.noNewExchanges = true; eviction.
if (references.isEmpty()) {
connection.idleAtNanos = now - keepAliveDurationNs;
return 0;
}
}
return references.size();
}
// 鏈接失敗時調用將失敗的Route放入到RouteDatabase中
public void connectFailed(Route failedRoute, IOException failure) {
// Tell the proxy selector when we fail to connect on a fresh connection.
if (failedRoute.proxy().type() != Proxy.Type.DIRECT) {
Address address = failedRoute.address();
address.proxySelector().connectFailed(
address.url().uri(), failedRoute.proxy().address(), failure);
}
routeDatabase.failed(failedRoute);
}
}
複製代碼
不論是同步請求仍是異步請求內部都是通過從上到下5個攔截器才能發起請求,獲取到響應後還會通過這5個攔截器而後纔將結果返回到外界,典型的責任鏈模式與Android事件分發差很少