OkHttp相信搞android的都不陌生,它是目前應(yīng)用最多的網(wǎng)絡(luò)請求開源框架须床,雖然現(xiàn)在Retrofit更加流行铐料,但到底層其實也是基于OkHttp的。你要是說你沒用過OkHttp都不好意思說自己是做過Android開發(fā)豺旬。那么今天就來聊聊OkHttp余赢。
本文的要點如下:
- 概述
- OkHttp的使用
- 源碼分析
- 同步請求
- 異步請求
- 總結(jié)
概述
OkHttp是一個網(wǎng)絡(luò)請求開源庫,即將網(wǎng)絡(luò)請求的相關(guān)功能封裝好的類庫哈垢。要知道妻柒,沒有網(wǎng)絡(luò)請求框架之前,App想與服務(wù)器進(jìn)行網(wǎng)絡(luò)請求交互是一件很痛苦的事耘分,因為Android的主線程不能進(jìn)行耗時操作举塔,那么就需另開1個線程請求、考慮到線程池求泰,緩存等一堆問題央渣。
于是乎,網(wǎng)絡(luò)請求庫出現(xiàn)了渴频,網(wǎng)絡(luò)請求庫的本質(zhì)其實是封裝了 網(wǎng)絡(luò)請求 + 異步 + 數(shù)據(jù)處理功能的庫芽丹。
OkHttp的使用
同步請求:
GET請求:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
POST請求:
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("username","abc")
.add("password","123456")
.build();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
異步請求(以GET為例):
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
使用這一塊沒什么好講的,不同類型的請求方式有些許的不同卜朗,不過都比較好理解拔第。下面我們還是來看看源碼中究竟是怎么做的吧咕村。
源碼分析
ps:我用的源碼是OkHttp3.14.1。
首先蚊俺,使用OkHttp時懈涛,必然要先創(chuàng)建OkHttpClient對象。
OkHttpClient client = new OkHttpClient();
一行代碼就搞定了泳猬,似乎有點過于簡單了批钠,我們來看看OkHttpClient()里面做了什么:
public OkHttpClient() {
this(new Builder());
}
原來是方便我們使用,提供了一個默認(rèn)的配置得封,直接傳入了一個默認(rèn)的Builder類型的對象埋心。Builder在初始化時就會設(shè)置這一些參數(shù),全都是默認(rèn)值忙上。這里就是運用了Builder模式踩窖,簡化了構(gòu)建過程。
public Builder() {
dispatcher = new Dispatcher();//調(diào)度器
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
同步請求
盡管現(xiàn)在項目中很少用同步請求了晨横,但是其實異步請求的基礎(chǔ)還是同步請求洋腮,只不過中間轉(zhuǎn)換了線程而已。
在同步請求手形,初始化之后啥供,我們又用Builder構(gòu)建了Request對象,然后執(zhí)行了OKHttpClient的newCall方法库糠,那么咱們就看看這個newCall里面都做什么操作伙狐?
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
可以看出client.newCall(request).execute();實際上執(zhí)行的是RealCall的execute方法,現(xiàn)在咱們再回來看下RealCall的execute的具體實現(xiàn)瞬欧。
@Override
public Response execute() throws IOException {
synchronized (this) {
//同步判斷贷屎,保證每個call只用一次,重復(fù)使用會拋出異常
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);
}
}
這里主要做了 4 件事:
- 檢查這個 call 是否已經(jīng)被執(zhí)行了艘虎,每個 call 只能被執(zhí)行一次唉侄,如果想要一個完全一樣的 call,可以利用call#clone方法進(jìn)行克隆野建。
- 利用client.dispatcher().executed(this)來進(jìn)行實際執(zhí)行属划,dispatcher是剛才看到的OkHttpClient.Builder的成員之一。
- 調(diào)用getResponseWithInterceptorChain()函數(shù)獲取 HTTP 返回結(jié)果候生,從函數(shù)名也可以看出同眯,這一步還會進(jìn)行一系列“攔截”操作。
- 最后還要通知dispatcher自己已經(jīng)執(zhí)行完畢唯鸭,釋放dispatcher须蜗。
那么我們看下dispatcher里面的execute()是如何處理的。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到,其實也很簡單明肮,runningSyncCalls執(zhí)行了add方法菱农,添加的參數(shù)是RealCall。runningSyncCalls是什么呢晤愧?
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
可以看到runningSyncCalls是雙向隊列大莫。另外蛉腌,我們發(fā)現(xiàn)Dispatcher里面定義了三個雙向隊列官份,看下注釋,我們大概能明白readyAsyncCalls是一個存放了等待執(zhí)行任務(wù)Call的雙向隊列烙丛,runningAsyncCalls是一個存放異步請求任務(wù)Call的雙向任務(wù)隊列舅巷,runningSyncCalls是一個存放同步請求的雙向隊列。
那么這一步的目的就是將RealCall放入同步隊列中河咽。
回到之前钠右,執(zhí)行完client.dispatcher().executed()方法,要執(zhí)行g(shù)etResponseWithInterceptorChain()方法忘蟹,這就是OkHttp的核心攔截器的工作了:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//添加開發(fā)者應(yīng)用層自定義的Interceptor
interceptors.addAll(client.interceptors());
//這個Interceptor是處理請求失敗的重試飒房,重定向
interceptors.add(new RetryAndFollowUpInterceptor(client));
//這個Interceptor工作是添加一些請求的頭部或其他信息
//并對返回的Response做一些友好的處理(有一些信息你可能并不需要)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//這個Interceptor的職責(zé)是判斷緩存是否存在,讀取緩存媚值,更新緩存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//這個Interceptor的職責(zé)是建立客戶端和服務(wù)器的連接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//添加開發(fā)者自定義的網(wǎng)絡(luò)層攔截器
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 {
//把chain傳遞到第一個Interceptor手中
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);
}
}
}
可以看到狠毯,主要的操作就是new了一個ArrayList,然后就是不斷的add攔截器Interceptor褥芒,之后new了一個RealInterceptorChain對象嚼松,最后調(diào)用了chain.proceed()方法。
我們來看看RealInterceptorChain()構(gòu)造方法锰扶。
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, Connection connection, int index, Request request) {
this.interceptors = interceptors;//將攔截鏈保存
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
就是一些賦值操作献酗,將信息保存,關(guān)鍵的是this.interceptors = interceptors這里就保存了攔截鏈坷牛。
之后我們來看一下chain.proceed()方法獲取返回的信息罕偎。由于Interceptor是個接口,所以應(yīng)該是具體實現(xiàn)類RealInterceptorChain的proceed實現(xiàn)京闰。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {
//省略其他代碼
calls++;
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//省略其他代碼
return response;
}
然后看到在proceed方面里面又new了一個RealInterceptorChain類的next對象锨亏,這個next對象和chain最大的區(qū)別就是index屬性值不同chain是0,而next是1,然后取interceptors下標(biāo)為1的對象的interceptor忙干。由從上文可知器予,如果沒有開發(fā)者自定義的應(yīng)用層Interceptor時,首先調(diào)用的RetryAndFollowUpInterceptor捐迫,如果有開發(fā)者自己定義的應(yīng)用層interceptor則調(diào)用開發(fā)者interceptor乾翔。
后面的流程都差不多,在每一個interceptor的intercept方法里面都會調(diào)用chain.proceed()從而調(diào)用下一個interceptor的intercept(next)方法,這樣就可以實現(xiàn)遍歷getResponseWithInterceptorChain里面interceptors的item反浓,實現(xiàn)遍歷循環(huán)萌丈。
之前我們看過getResponseWithInterceptorChain里面interceptors的最后一個item是CallServerInterceptor,最后一個Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是進(jìn)行繼續(xù)遞歸雷则。
CallServerInterceptor返回response后返回給上一個interceptor,一般是開發(fā)者自己定義的networkInterceptor辆雾,然后開發(fā)者自己的networkInterceptor把他的response返回給前一個interceptor,依次以此類推返回給第一個interceptor月劈,這時候又回到了realCall里面的execute()里面了度迂。
最后把response返回給get請求的返回值。至此同步GET請求的大體流程就已經(jīng)結(jié)束了猜揪。
異步請求
講了這么多同步請求惭墓,其實異步請求才是重頭戲,畢竟現(xiàn)在的項目中大多用的都是異步請求而姐。
由于前面的步驟和同步一樣new了一個OKHttp和Request腊凶。這塊和同步一樣就不說了,那么說說和同步不一樣的地方拴念,后面異步進(jìn)入的是newCall()的enqueue()方法钧萍。
之前分析過,newCall()里面是生成了一個RealCall對象政鼠,那么執(zhí)行的其實是RealCall的enqueue()方法风瘦。我們來看源碼:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
//同步判斷,保證每個call只用一次缔俄,重復(fù)使用會拋出異常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
可以看到和同步請求的enqueue方法一樣弛秋,還是先同步判斷是否被請求過了,不一樣的地方就在于調(diào)用了client.dispatcher().enqueue(new AsyncCall(responseCallback))方法俐载。即實際調(diào)用的是Dispatcher的enqueue()方法:
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);//將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();
}
這里主要做了兩件事:
- 將call添加到了異步請求隊列蟹略;
- 調(diào)用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);//將異步請求添加到executableCalls中
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后添加到了executableCalls集合中。然后遍歷這個集合雪情,開始執(zhí)行每個AsyncCall的executeOn方法。參數(shù)是executorService()意敛,我們來具體看看:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
不難看出,這個方法傳遞進(jìn)去的是ExecutorService線程池膛虫。那看來關(guān)鍵就在executeOn方法中了:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);//用executorService線程池執(zhí)行了當(dāng)前的線程
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!
}
}
}
通過executorService線程池執(zhí)行了當(dāng)前的線程草姻,也就是AsyncCall,那么AsyncCall由于繼承了NamedRunnable稍刀,這個NamedRunnable的run方法里又執(zhí)行了抽象方法execute撩独,所以敞曹,實際上這里執(zhí)行了AsyncCall的execute方法。
@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);
}
}
終于综膀,我們看到了幾個熟悉的名字澳迫,getResponseWithInterceptorChain、onResponse和onFailure剧劝。不難看出橄登,這里依舊是用getResponseWithInterceptorChain()通過攔截鏈進(jìn)行請求,最終執(zhí)行結(jié)果回調(diào)給了我們傳遞進(jìn)去的Callback讥此。至此拢锹,異步請求的主要流程也分析完了。
對比一下同步請求和異步請求暂论,不難看出面褐,其實異步請求就是維護(hù)了一個線程池用于進(jìn)行請求拌禾,在請求完成之后回調(diào)我們一開始傳入的CallBack接口取胎。
總結(jié)
- OkHttpClient實現(xiàn)了Call.Factory,負(fù)責(zé)為Request創(chuàng)建Call湃窍;
- RealCall為具體的Call實現(xiàn)闻蛀,execute()為同步接口,通過getResponseWithInterceptorChain()函數(shù)實現(xiàn)您市;enqueue()為異步接口通過Dispatcher利用ExecutorService線程池實現(xiàn)觉痛,而最終進(jìn)行網(wǎng)絡(luò)請求時和同步接口一致,都是通過getResponseWithInterceptorChain()函數(shù)實現(xiàn)茵休;
- getResponseWithInterceptorChain()中利用攔截鏈機(jī)制薪棒,分層實現(xiàn)緩存、透明壓縮榕莺、網(wǎng)絡(luò) IO 等功能俐芯。