一 為什么推薦使用Okhttp3?
首先,我并不覺得OkHttp是一個(gè)網(wǎng)絡(luò)框架。okhttp對(duì)標(biāo)的,應(yīng)該是HttpClient或者HttpURLConnection,okhttp應(yīng)該是一種新的網(wǎng)絡(luò)請(qǐng)求方法,而網(wǎng)絡(luò)框架,應(yīng)該是基于上面幾個(gè)網(wǎng)絡(luò)訪問方式進(jìn)行封裝的。像volley(基于httpClient和httpURLConnection)或者retrofit2(基于OkHttp3)。
好吧,扯遠(yuǎn)了~~
我們?yōu)槭裁匆褂肙kHttp3作為我們新的網(wǎng)絡(luò)請(qǐng)求方式?我認(rèn)為有以下幾點(diǎn):
1 OkHttp支持SPDY、http2.0和https
2 OkHttp3內(nèi)置ConnectionPool連接池,可以實(shí)現(xiàn)多路復(fù)用(在spdy不可用的情況下使用)
3 利用GZip壓縮內(nèi)容
4 具備超時(shí)重連機(jī)制,調(diào)用方不用自己去進(jìn)行自定義連接動(dòng)作
5 具備緩存機(jī)制,可以避免重復(fù)的網(wǎng)絡(luò)請(qǐng)求
6 使用簡(jiǎn)單,封裝的很好,方便調(diào)用(這個(gè)吧,不知道算不算,哈哈哈)
二 OKHttp3.0集成
集成方式還是比較簡(jiǎn)單的,我們只需要在我們的app的build.gradle中添加依賴
compile 'com.squareup.okhttp3:okhttp:3.10.0'
同步調(diào)用:
private void sendRequestSync() throws IOException {
OkHttpClient mHttpClient=new OkHttpClient();
Request request= new Request.Builder().url(ENDPOINT).build();
mHttpClient.newCall(request).execute();
}
異步調(diào)用:
private void sendRequestAsyn(){
OkHttpClient mHttpClient=new OkHttpClient();
Request request= new Request.Builder().url(ENDPOINT).build();
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String json=response.body().string();
}
});
}
三 OkHttp3請(qǐng)求流程分析
由于同步執(zhí)行的代碼比較簡(jiǎn)單直觀,我們這里用異步請(qǐng)求的方式進(jìn)行分析。首先我們把整個(gè)網(wǎng)絡(luò)請(qǐng)求分為4個(gè)部分
3.1 OkHttpClient的初始化
OkHttpClient mHttpClient=new OkHttpClient();
OKHttpClient其實(shí)是call的一個(gè)工廠類,OkHttpClient是用來發(fā)起http請(qǐng)求,并拿到http的response的處理類。因?yàn)槊恳粋€(gè)HttpClient都維護(hù)有自己的連接池和線程池,所以不建議在每一次請(qǐng)求的時(shí)候都去初始化一個(gè)OkHttpcliet,而是在我們的項(xiàng)目中,只維護(hù)一個(gè)單例類就可以。
我們有兩種方法去初始化一個(gè)OkHttpClient:
方法一:直接new OkHttpClient(),創(chuàng)建一個(gè)默認(rèn)的OkHttpClient;
方法二:通過builder去創(chuàng)建:
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.build();
3.2 異步方法執(zhí)行
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String json=response.body().string();
}
});
在這里,我們首先去查看newcall(request)方法
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);}
所以,我們知道,最終的enqueue方法是在realCall類中調(diào)用的。我們前去查看源碼
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));}
從這段代碼的最后一個(gè)client.dispatcher(),好了到這里,我們就需要去看看Dispatcher類了。
3.3:Dispatcher類
Dispatcher是OkHttp的異步請(qǐng)求策略類,在這里,OkHttp3定義了多個(gè)重要的參數(shù),分別是
public final class Dispatcher {
//最大的請(qǐng)求數(shù)量
private int maxRequests = 64;
//每一個(gè)host的最大請(qǐng)求數(shù)
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
private @Nullable ExecutorService executorService;
//當(dāng)前準(zhǔn)備執(zhí)行的隊(duì)列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//當(dāng)前正在執(zhí)行的隊(duì)列,包括那些尚未結(jié)束的請(qǐng)求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//同步執(zhí)行的隊(duì)列,包括還沒有結(jié)束的請(qǐng)求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
}
dispatch中查看enqueue的處理方式
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
也就是,當(dāng)前的運(yùn)行隊(duì)列小于最大值maxRequest并且當(dāng)前運(yùn)行的每個(gè)host的請(qǐng)求小于host最大請(qǐng)求數(shù)的時(shí)候,我們就把當(dāng)前的call加入到執(zhí)行隊(duì)列中,否則就加入到等待隊(duì)列中,等待okHttp執(zhí)行。那么,我們是在哪里執(zhí)行這些隊(duì)列中的請(qǐng)求的呢?答案是RealCall
3.4:RealCall
RealCall里面的內(nèi)部類AsyncCall封裝了異步執(zhí)行下的execute()方法。話不多少,我們直接上源碼
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
這段代碼,我們只關(guān)注兩個(gè)核心的方法,一個(gè)是getResponseWithInterceptorChain(),另一個(gè)是client.dispatcher().finished(this);
3.4.1: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);
}
我們從execute()方法可以知道,我們真正發(fā)出網(wǎng)絡(luò)請(qǐng)求,獲取response方法就是在這個(gè)方法中。我們一步一步分析:
第一:多攔截器的構(gòu)造。
getResponseWithInterceptorChain()方法首先定義了一個(gè)攔截器list,從代碼中我們可以看到,添加的順序分別為:
1 移動(dòng)端自定義的攔截器:client.interceptors()(為什么自定義的攔截器要第一個(gè)添加,后面有介紹)
2 retryAndFollowUpInterceptor 失敗重試的攔截器
3 BridgeInterceptor:官方解釋,這是一個(gè)連接我們應(yīng)用和網(wǎng)絡(luò)的橋梁。首先通過用戶請(qǐng)求創(chuàng)建一個(gè)網(wǎng)咯請(qǐng)求,接著把這個(gè)請(qǐng)求發(fā)出處理,最后把網(wǎng)絡(luò)返回的值轉(zhuǎn)換成用戶所需要的值
4 CacheInterceptor:緩存攔截器
5 ConnectInterceptor:網(wǎng)絡(luò)請(qǐng)求攔截器。用來跟服務(wù)端進(jìn)行連接
6 CallServerInterceptor:這個(gè)是我們網(wǎng)絡(luò)請(qǐng)求的鏈?zhǔn)秸{(diào)用的最后一個(gè)攔截器,用來將數(shù)據(jù)丟到網(wǎng)絡(luò)中進(jìn)行處理
第二:RealInterceptorChain 初始化,鏈?zhǔn)秸{(diào)用正式開始
RealInterceptorChain類我們主要看process()方法。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 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);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
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;
}
前面的異常處理邏輯我們一律不看。我們主要看這一段
// 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);
我們?cè)谇癛ealCall中AsynCall的execute()方法中,傳入RealInterceptorChain初始化的時(shí)候,index是0的,也就是說,上面的interceptors.get(index);其實(shí)等于interceptors.get(0)=client.interceptors(),也即是我們移動(dòng)端自定義的攔截器。最后通過
Response response = interceptor.intercept(next);
我們可以看到,最后回到的,就是各個(gè)自定義攔截器中的intercept()方法中(各位如果不理解,可以去看看官方demo里面HttpLoggingInterceptor.java的實(shí)現(xiàn))。
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
這段代碼就是為了回調(diào)前面我們調(diào)用enqueue的時(shí)候的listener,返回當(dāng)前的網(wǎng)絡(luò)請(qǐng)求是成功的,還是失敗的。這個(gè)簡(jiǎn)單,暫且不做分析。
3.4.3:client.dispatcher().finished(this);
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
我們可以看到,無論是同步還是異步執(zhí)行網(wǎng)絡(luò)請(qǐng)求,最終都會(huì)調(diào)用到這個(gè)finish方法中。這個(gè)就是OkHttp維護(hù)當(dāng)前連接池的方法,每當(dāng)一個(gè)請(qǐng)求結(jié)束后,都會(huì)把當(dāng)前的call從當(dāng)前的運(yùn)行隊(duì)列中移除。然后再執(zhí)行promoteCalls(),把等待運(yùn)行隊(duì)列中的下一個(gè)請(qǐng)求放入運(yùn)行隊(duì)列中。
四 總結(jié)
自此,OkHttp3.0的執(zhí)行流程分析到此結(jié)束。但是OkHttp的內(nèi)容遠(yuǎn)不止這么復(fù)雜,接下來我會(huì)在下一篇文章,對(duì)OkHttp的其他技術(shù)細(xì)節(jié)結(jié)合源碼進(jìn)行詳細(xì)分析。