OKHttp源碼解讀
本次分享主要目標
- 1.大概流程解讀,同步流程,異步流程
- 2.Interceptor簡讀
一、OKHttp的簡單使用
private String get(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
private void useOkHttp() throws IOException {
//1.創(chuàng)建OkHttpClient對象
OkHttpClient okhttp = new OkHttpClient.Builder()
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
//2.創(chuàng)建請求對象Request
Request request = new Request.Builder()
.url("url")
.addHeader("key","value")
.get()//設置請求方式是get
.build();
Call call = okhttp.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
String string = body.string();
}
});
}
以上是OKHttp很簡單的兩個用法,一個是同步的,一個是異步的。
二、回顧Retrofit源碼與OKhttp的初始化
- Rerofit的初始化
- ServiceMethon的初始化
- 調用OKHttp
- 裝飾器 adapt的使用
OKHttp與Retrofit都是同一幫人寫的,所以寫法是類似的。
Builder重度使用,OKHttpCient與Retrofit的作用類似。
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int callTimeout;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
請求過程中用到的各種設置都可以從這里拿到,Retrofit的next方法是不是還有印象,后面的各種方法都是有retrofit的參數(shù)傳進去,獲取初始化參數(shù)。
三、執(zhí)行過程簡析
Call call = okhttp.newCall(request);
call.enqueue
call.execute
這個是先拿到一個request,然后excute或者enqueue
1.我們先分析同步的代碼流程
@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);
}
}
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
client.dispatcher().executed(this);
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
我們總結總結同步的流程
- RealCall.excute
- client.dispatcher().executed
- RealCall.getResponseWithInterceptorChain
- client.dispatcher().finished;
同步執(zhí)行的時候,runningSyncCalls 進行了添加刪除用作記錄,目前沒有數(shù)量控制,也沒有線程調度什么。
看到這個Call,有沒有想到Retrofit里的Call?
- Retrofit Call接口,OKHttpCall
- OKHttp里也是有個Call接口,RealCall,作為他的實現(xiàn)類,還有一個AsyncCall,但是這個不是Call的實現(xiàn)類,復制RealCall做異步使用的。
2.異步流程解析
RealCall.enqueue
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher.enqueue
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
Dispatcher.promoteAndExecute
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();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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.executeOn(executorService());
}
return isRunning;
}
AsyncCall.executeOn
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!
}
}
}
AsyncCall.execute->RealCall.getResponseWithInterceptorChain()->AsyncCall.responseCallback
@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) {
// 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);
}
}
我們總結總結異步的流程
- RealCall.enqueue
- Dispatcher.enqueue
- Dispatcher.promoteAndExecute
- AsyncCall.executeOn
- AsyncCall.execute
- RealCall.getResponseWithInterceptorChain
- AsyncCall.responseCallback
從流程上來看的話,不管是同步異步實際請求都是調用
RealCall.getResponseWithInterceptorChain
只是異步請求Dispatcher 做了線程管理,這塊內容下次再做分享
三、Interceptor簡析
getResponseWithInterceptorChain的實現(xiàn)
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
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);
}
}
}
這里的核心是
- interceptors.add
- Response response = chain.proceed(originalRequest);
添加攔截器,執(zhí)行攔截器。
攔截器的執(zhí)行采用的是遞歸原理,理解了遞歸原理的話,攔截器這塊就沒什么內容了,就是解讀各個攔截器的實現(xiàn)。
我們看下一個簡單的遞歸例子
function Recursion(depth) {
console.log('抱著');
if (!depth) {
console.log('我的小鯉魚')
} else {
Recursion(--depth); // 遞歸調用
}
console.log('的我');
}
console.log('嚇得我抱起了');
Recursion(2)
日志打印出來
- 嚇得我抱起了
- 抱著
- 抱著
- 抱著
- 我的小鯉魚
- 的我
- 的我
- 的我
遞歸的特點
- 遞歸結束的條件
- 執(zhí)行順序,前置的按照順序從上往下執(zhí)行,結果是從下往上
這里是不是可以理解,每個interceptor都能拿到網絡請求后的response結果了,因為最后一個拿到的response,然后從下往上每個都能拿到了。

Interceptor的原理明白了嗎?
也就是開始被執(zhí)行的類request的信息是做少的,越執(zhí)行reqeust信息越多,response,則是最后被執(zhí)行的的是最原始的,然后被修改添加的越多。
我們再來學習下Interceptor的1個知識點
- interceptor與networkInterceptor的區(qū)別
- RetryAndFollowUpInterceptor代碼解讀
通過 addInterceptor() 方法添加的攔截器是放在最前面的。而通過 addNetworkInterceptor() 方法添加的網絡攔截器,則是在非 WebSocket 請求時,添加在 ConnectInterceptor 和 CallServerInterceptor 之間的。
addInterceptor(應用攔截器):
- 不需要擔心中間過程的響應,如重定向和重試.
- 總是只調用一次,即使HTTP響應是從緩存中獲取.
- 觀察應用程序的初衷. 不關心OkHttp注入的頭信息如: If-None-Match.
- 允許短路而不調用 Chain.proceed(),即中止調用.
- 允許重試,使 Chain.proceed()調用多次.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
addNetworkInterceptor(網絡攔截器):- 能夠操作中間過程的響應,如重定向和重試.
- 當網絡短路而返回緩存響應時不被調用.
- 只觀察在網絡上傳輸?shù)臄?shù)據(jù).
- 攜帶請求來訪問連接.
RetryAndFollowUpInterceptor
@Override 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) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
Request followUp = followUpRequest(response, route);
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;
}
}
這里有個比較關關鍵的一點
while (true) {
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
response = realChain.proceed(request, transmitter, null);
while(true) 這里是跟其他interceptor不一樣的地方,對于重定向大家有沒有了解?
退出while的條件
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
一個是返回后沒有再重定向了,另外一個則是重定向次數(shù)超過了閾值
注意點
重定向執(zhí)行的時候,是沒有再調用Interceptor,但是netInterceptor還是被調用的。
本次分享到此結束,下次詳解,dispatch與其他interceptor