概述
okhttp,一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開源項(xiàng)目,是安卓端最火熱的輕量級(jí)框架,由移動(dòng)支付Square公司貢獻(xiàn)用于替代HttpUrlConnection和Apache HttpClient,而Retrofit也是Square開源的一款適用于Android網(wǎng)絡(luò)請(qǐng)求的框架。Retrofit底層是基于OkHttp實(shí)現(xiàn)的,與其他網(wǎng)絡(luò)框架不同的是,它更多使用運(yùn)行時(shí)注解的方式提供功能。
現(xiàn)在比較常用的網(wǎng)絡(luò)請(qǐng)求方式大概有四種:Android-Async-Http、Volley、OkHttp、Retrofit,下面借用一張圖來(lái)讓你大概了解全他們的特點(diǎn)和他們之間的區(qū)別

HttpClient簡(jiǎn)介:
HttpClient 是Apache的一個(gè)三方網(wǎng)絡(luò)框架,網(wǎng)絡(luò)請(qǐng)求做了完善的封裝,api眾多,用起來(lái)比較方便,開發(fā)快。實(shí)現(xiàn)比較穩(wěn)定,bug比較少,但是正式由于其api眾多,是我們很難再不破壞兼容性的情況下對(duì)其進(jìn)行擴(kuò)展。所以,Android團(tuán)隊(duì)對(duì)提升和優(yōu)化httpclient積極性并不高。android5.0被廢棄,6.0逐漸刪除。
HttpURLConnection簡(jiǎn)介
HttpURLConnection是一個(gè)多用途、輕量級(jí)的http客戶端。它對(duì)網(wǎng)絡(luò)請(qǐng)求的封裝沒有HttpClient徹底,api比較簡(jiǎn)單,用起來(lái)沒有那么方便。但是正是由于此,使得我們能更容易的擴(kuò)展和優(yōu)化的HttpURLConnection。不過(guò),再android2.2之前一直存在著一些令人煩的bug,比如一個(gè)人可讀的inputstream調(diào)用它的close方法的時(shí)候,會(huì)使得連接池實(shí)效,通常的做法就是禁用連接池。因此,在android2.2之前建議使用穩(wěn)定的HttpClient,android2.2之后使用更容易擴(kuò)展和優(yōu)化的HttpURLConnection。
這篇文章我們先說(shuō)說(shuō)okhttp
OkHttp官網(wǎng)地址:http://square.github.io/okhttp/
OkHttp GitHub地址:https://github.com/square/okhttp
基本使用
配置
導(dǎo)入Jar包
點(diǎn)擊下面鏈接下載最新 JAR
http://square.github.io/okhttp/#download
或者
GRADLE
在build.gradle中引用compile 'com.squareup.okhttp3:okhttp:(insert latest version)'
使用
在日常開發(fā)中最常用到的網(wǎng)絡(luò)請(qǐng)求就是GET和POST兩種請(qǐng)求方式。
HTTP GET
創(chuàng)建一個(gè)普通的同步get請(qǐng)求代碼如下:
String run(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
異步get:
Request request = new Request.Builder()
.url("http://xxxxxx")
.build();
OkHttpClient client = new OkHttpClient();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){//回調(diào)的方法執(zhí)行在子線程。
}
}
});
HTTP POST
創(chuàng)建一個(gè)普通的post提交json數(shù)據(jù)代碼如下:
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
Request是OkHttp中訪問(wèn)的請(qǐng)求,Builder是輔助類,Response即OkHttp中的響應(yīng)。 從上面可以看出發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求主要三個(gè)步驟:1.創(chuàng)建OkHttpClient實(shí)例;2.使用構(gòu)造器創(chuàng)建請(qǐng)求;3.提交請(qǐng)求接收返回?cái)?shù)據(jù)。整個(gè)處理流程如下:

通過(guò)使用我們?cè)诰唧w分析下我們使用的功能在內(nèi)部是怎么實(shí)現(xiàn)的:
首先創(chuàng)建一個(gè)Request請(qǐng)求,設(shè)置相應(yīng)的url,和具體的請(qǐng)求參數(shù)和一些其他的配置,Request是通過(guò)Builder來(lái)完成構(gòu)建的。雖然說(shuō)是構(gòu)建Request,但實(shí)際上這些數(shù)據(jù)是不足以構(gòu)建一個(gè)合法的Request的,其他待補(bǔ)全的信息其實(shí)是OkHttp在后面某個(gè)環(huán)節(jié)幫你加上去,我們開發(fā)者只需要設(shè)置一些必要的參數(shù)就行了。
創(chuàng)建一個(gè)OkHttpClient 對(duì)象
OkHttpClient client = new OkHttpClient();
進(jìn)入源碼我們可以看到她的構(gòu)造方法
public OkHttpClient() {
this(new Builder());
}
public Builder() {
dispatcher = new Dispatcher(); //請(qǐng)求的執(zhí)行器,負(fù)責(zé)請(qǐng)求的同步執(zhí)行、異步執(zhí)行、取消、以及最多可以同時(shí)執(zhí)行的請(qǐng)求數(shù)
protocols = DEFAULT_PROTOCOLS; //支持的Protocol協(xié)議,Protocol和Url類似
connectionSpecs = DEFAULT_CONNECTION_SPECS; //支持套接字連接協(xié)議.
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault(); //代理服務(wù)器選擇器
cookieJar = CookieJar.NO_COOKIES; //提供cookie可持久化操作。
socketFactory = SocketFactory.getDefault(); //創(chuàng)建套接字的工廠類
hostnameVerifier = OkHostnameVerifier.INSTANCE; //主機(jī)名驗(yàn)證
certificatePinner = CertificatePinner.DEFAULT; //約束所信任的證書
proxyAuthenticator = Authenticator.NONE; //身份驗(yàn)證代理服務(wù)器
authenticator = Authenticator.NONE; //身份驗(yàn)證代理服務(wù)器
connectionPool = new ConnectionPool(); //連接池用于回收利用HTTP和HTTPS連接。
dns = Dns.SYSTEM; //dns服務(wù)器;默認(rèn)使用系統(tǒng)的
followSslRedirects = true; //是否支持sslhttp重定向,默認(rèn)true
followRedirects = true; //是否支持http重定向,默認(rèn)true
retryOnConnectionFailure = true; //連接失敗時(shí)是否重試,默認(rèn)true
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
為了方便我們使用,okhttp提供了一個(gè)“快捷操作”,全部使用了默認(rèn)的配置,比如指定了 Dispatcher (管理線程池)、鏈接池、超時(shí)時(shí)間等。OkHttpClient依然是通過(guò)Builder來(lái)進(jìn)行構(gòu)建的,我們開發(fā)者也可以自己去設(shè)置一些參數(shù),比如:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(new ParamInterceptor(mApplication))
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
值得注意的是OKHttpClient實(shí)現(xiàn)了 Call.Factory接口,創(chuàng)建一個(gè)RealCall類的實(shí)例(Call的實(shí)現(xiàn)類)。前面的請(qǐng)求我們看到,在發(fā)送請(qǐng)求之前,需要調(diào)用newCall()方法,創(chuàng)建一個(gè)指向RealCall實(shí)現(xiàn)類的Call對(duì)象,實(shí)際上RealCall包裝了Request和OKHttpClient這兩個(gè)類的實(shí)例。使得后面的方法中可以很方便的使用這兩者。
創(chuàng)建完Request和OKHttpClient,接著我們看下具體是怎樣發(fā)起請(qǐng)求的,根據(jù)流程圖我們看到我們先有一個(gè)RealCall對(duì)象,接著看看RealCall對(duì)象是怎么被創(chuàng)建的,OKHttpClient實(shí)現(xiàn)了Call.Factory接口創(chuàng)建了一個(gè)call對(duì)象:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
通過(guò)前面代碼可以看到得到RealCall對(duì)象,執(zhí)行同步的execute()方法或者異步的enqueue我們就可以發(fā)起請(qǐng)求了,雖然只有一個(gè)簡(jiǎn)簡(jiǎn)單單的方法,但是這里面卻是最為復(fù)雜的,我們來(lái)看看我 RealCall里面的execute()方法和enqueue方法:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
@Override
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));
}
enqueue方法里面引入了一個(gè)新的類AsyncCall,這個(gè)類繼承于NamedRunnable,實(shí)現(xiàn)了Runnable接口。NamedRunnable可以給當(dāng)前的線程設(shè)置名字,并且用模板方法將線程的執(zhí)行體放到了execute方法中。
enqueue/execute 方法中涉及好幾個(gè)新的類和方法,如下:
同步 execute:
captureCallStackTrace()
dispatcher()
getResponseWithInterceptorChain()
異步 enqueue:
captureCallStackTrace()
dispatcher()
AsyncCall
我們看到不管是同步還是異步都會(huì)調(diào)用兩個(gè)相同的方法 captureCallStackTrace() 和 dispatcher() ,先看下 captureCallStackTrace() ,通過(guò)看源碼這個(gè)應(yīng)該是追蹤棧信息的。
Dispatcher是做什么的呢?文章開頭就提到了okhttp有個(gè)優(yōu)勢(shì)就是內(nèi)置連接池,支持連接復(fù)用,減少延遲。Dispatcher類就是負(fù)責(zé)管理調(diào)度的,我們可以看看Dispatcher的源碼:
public final class Dispatcher {
/** 最大并發(fā)請(qǐng)求數(shù)為64 */
private int maxRequests = 64;
/** 每個(gè)主機(jī)最大請(qǐng)求數(shù)為5 */
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** 線程池 */
private @Nullable ExecutorService executorService;
/** 準(zhǔn)備執(zhí)行的異步請(qǐng)求 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 正在執(zhí)行的異步請(qǐng)求,包含已經(jīng)取消但未執(zhí)行完的請(qǐng)求 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 正在執(zhí)行的同步請(qǐng)求,包含已經(jīng)取消單未執(zhí)行完的請(qǐng)求 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
.....
}
從ExecutorService executorService()方法可以看出線程池實(shí)現(xiàn)了對(duì)象復(fù)用,降低線程創(chuàng)建開銷,從設(shè)計(jì)模式上來(lái)講,使用了享元模式。
代碼太多了,我只貼出了部分,有興趣大家可以去研究下,代碼不多。言歸正傳,從源碼我們可以看出Dispatcher就像是一個(gè)調(diào)度中心,這里負(fù)責(zé)所有的任務(wù)執(zhí)行,值得注意的是我們看到executorService這個(gè)對(duì)象是個(gè)線程池,配置如下:
核心線程數(shù):0
最大線程數(shù):Iteger.MAX_VALUE,其實(shí)就是不限制
空閑線程保活時(shí)間:60s
任務(wù)隊(duì)列:SynchronousQueue(沒有緩存大小的阻塞隊(duì)列),為有界隊(duì)列,且隊(duì)列大小為 0,不存 Runnable,只是用來(lái)進(jìn)行生產(chǎn)者和消費(fèi)者之間的傳遞任務(wù)。
實(shí)際上這里和我們?cè)?Executors 使用的 緩存線程池的配置完全一樣的,唯一的區(qū)別僅僅是修改了線程名,我們簡(jiǎn)要分析一下緩存線程池的運(yùn)行機(jī)制,進(jìn)入到 ThreadPoolExecutor 中的 execute 方法的源碼中,假設(shè)線程池處于運(yùn)行狀態(tài),會(huì)有這樣的流程:
新過(guò)來(lái)一個(gè)任務(wù),由于核心線程數(shù)為 0,不需要?jiǎng)?chuàng)建核心線程,所以嘗試加入任務(wù)隊(duì)列
如果線程池中有線程剛好空閑可以接收任務(wù),因?yàn)槿蝿?wù)隊(duì)列的類型是 SynchronousQueue,實(shí)際大小為 0,只做消費(fèi)者和生產(chǎn)者的中轉(zhuǎn)站,所以這時(shí)候,就可以入列成功,通過(guò) SynchronousQueue 中轉(zhuǎn)任務(wù)給空閑的線程執(zhí)行。
如果當(dāng)前線程池所有線程工作飽和,會(huì)入列失敗。這時(shí)候 ThreadPoolExecutor 會(huì)調(diào)用 addWorker(command, false) 來(lái)創(chuàng)建并且啟動(dòng)新線程。
如果這樣就會(huì)產(chǎn)生一個(gè)問(wèn)題,如果客戶端一次發(fā)起1000個(gè)請(qǐng)求,那肯定就會(huì)產(chǎn)生1000個(gè)線程。所以在 OkHttp 中線程池只是一個(gè)輔助作用,僅僅是用來(lái)做線程緩存,便于復(fù)用的。真正對(duì)這些請(qǐng)求的并發(fā)數(shù)量限制,執(zhí)行時(shí)機(jī)等等都是調(diào)度器 Dispatcher 承擔(dān)的。在 OkHttp 這里,線程池只是個(gè)帶緩存功能的執(zhí)行器,而真正的調(diào)度是外部包了一個(gè)調(diào)度策略的:
首先,是線程數(shù)量的約束。最大并發(fā)數(shù) maxRequests 默認(rèn)為 64,單個(gè)主機(jī)最大請(qǐng)求數(shù) maxRequestsPerHost 默認(rèn)為 5 個(gè)。所以極端情況下,才會(huì)開啟 64 個(gè)線程。一般場(chǎng)景是不會(huì)出現(xiàn)的,如果超過(guò)了約束,我們從代碼里面可以看到有兩個(gè)異步請(qǐng)求隊(duì)列readyAsyncCalls 和runningAsyncCalls ,如果沒有超過(guò)約束則把異步請(qǐng)求加入 runningAsyncCalls ,并在線程池中執(zhí)行(線程池會(huì)根據(jù)當(dāng)前負(fù)載自動(dòng)創(chuàng)建,銷毀,緩存相應(yīng)的線程)。否則就會(huì)加入 readyAsyncCalls 緩沖排隊(duì)。
那么,等待隊(duì)列中的請(qǐng)求什么時(shí)候會(huì)執(zhí)行呢?
回到之前提到的 AsyncCalls 中,在 execute 結(jié)束的時(shí)候會(huì)再調(diào)用一下 Dispatcher 的 finished 方法,如下
繼續(xù)看源碼我們會(huì)看到在finished方法中會(huì)執(zhí)行一個(gè)方法

所以,OkHttp 的調(diào)度器默認(rèn)配置下,在保證每個(gè)域名可以快速響應(yīng)請(qǐng)求的情況下,還限制了每個(gè)域名的并發(fā)數(shù)和總體的并發(fā)數(shù)。實(shí)際每個(gè)應(yīng)用的請(qǐng)求用到的域名都不多。線程池僅僅做線程緩存功能,調(diào)度策略應(yīng)該外部自己去實(shí)現(xiàn)。
接著我們來(lái)分析下getResponseWithInterceptorChain()
getResponseWithInterceptorChain()應(yīng)該是最為復(fù)雜的,我們先來(lái)看看源碼:
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 開發(fā)者之一介紹 OkHttp 的文章里面,作者講到:the whole thing is just a stack of built-in interceptors.
可見 Interceptor 是 OkHttp 最核心的一個(gè)東西,不要誤以為它只負(fù)責(zé)攔截請(qǐng)求進(jìn)行一些額外的處理(例如 cookie),實(shí)際上它把實(shí)際的網(wǎng)絡(luò)請(qǐng)求、緩存、透明壓縮等功能都統(tǒng)一了起來(lái),每一個(gè)功能都只是一個(gè) Interceptor,它們?cè)龠B接成一個(gè) Interceptor.Chain,環(huán)環(huán)相扣,最終圓滿完成一次網(wǎng)絡(luò)請(qǐng)求。
從上面我們可以分析出,其邏輯大致分為兩部分:
1、創(chuàng)建一系列攔截器,并將其放入一個(gè)攔截器數(shù)組中。這部分?jǐn)r截器即包括用戶自定義的攔截器也包括框架內(nèi)部攔截器
2、創(chuàng)建一個(gè)攔截器鏈RealInterceptorChain,并執(zhí)行攔截器鏈的proceed方法
這里okhttp用到了一個(gè)很經(jīng)典的設(shè)計(jì)模式:責(zé)任鏈模式,它在okhttp中得到了很好的實(shí)踐。實(shí)際上責(zé)任鏈模式在安卓系統(tǒng)中也有比較典型的實(shí)踐,例如 view 系統(tǒng)對(duì)點(diǎn)擊事件(TouchEvent)的處理,有興趣可以去看下。
從 getResponseWithInterceptorChain 函數(shù)我們可以看到,借用一張圖,Interceptor.Chain 的分布依次是:

從流程圖我們可以看出
1、首先是在創(chuàng)建OkHttpClient 自定義的interceptors
2、用來(lái)實(shí)現(xiàn)連接失敗的重試和重定向的RetryAndFollowUpInterceptor(在網(wǎng)絡(luò)請(qǐng)求失敗后進(jìn)行重試
當(dāng)服務(wù)器返回當(dāng)前請(qǐng)求需要進(jìn)行重定向時(shí)直接發(fā)起新的請(qǐng)求,并在條件允許情況下復(fù)用當(dāng)前連接)
3、用來(lái)修改請(qǐng)求和響應(yīng)的 header 信息的BridgeInterceptor(設(shè)置內(nèi)容長(zhǎng)度,內(nèi)容編碼
設(shè)置gzip壓縮,并在接收到內(nèi)容后進(jìn)行解壓。省去了應(yīng)用層處理數(shù)據(jù)解壓的麻煩
添加cookie,設(shè)置其他報(bào)頭,如User-Agent,Host,Keep-alive等。其中Keep-Alive是實(shí)現(xiàn)多路復(fù)用的必要步驟)
4、負(fù)責(zé)讀取緩存直接返回、更新緩存的 CacheInterceptor(當(dāng)網(wǎng)絡(luò)請(qǐng)求有符合要求的Cache時(shí)直接返回Cache,當(dāng)服務(wù)器返回內(nèi)容有改變時(shí)更新當(dāng)前cache,如果當(dāng)前cache失效,刪除)
5、負(fù)責(zé)和服務(wù)器建立連接的 ConnectInterceptor(其實(shí)是調(diào)用了 StreamAllocation 的newStream 方法來(lái)打開連接的。建聯(lián)的 TCP 握手,TLS 握手都發(fā)生該階段。過(guò)了這個(gè)階段,和服務(wù)端的 socket 連接打通)
6、配置 OkHttpClient 時(shí)設(shè)置的 networkInterceptors
7、負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)的 CallServerInterceptor(上一個(gè)階段已經(jīng)握手成功,HttpStream 流已經(jīng)打開,所以這個(gè)階段把 Request 的請(qǐng)求信息傳入流中,并且從流中讀取數(shù)據(jù)封裝成 Response 返回)
這就構(gòu)成了okhttp最核心的一塊東西,把Request 變成 Response。每個(gè) Interceptor 都可能完成這件事,所以我們循著鏈條讓每個(gè) Interceptor 自行決定能否完成任務(wù)以及怎么完成任務(wù)(自力更生或者交給下一個(gè) Interceptor)。這樣一來(lái),完成網(wǎng)絡(luò)請(qǐng)求這件事就徹底從 RealCall 類中剝離了出來(lái),簡(jiǎn)化了各自的責(zé)任和邏輯。
在這里我們簡(jiǎn)單分析下CallServerInterceptor,看看 OkHttp 是怎么進(jìn)行和服務(wù)器的實(shí)際通信的
CallServerInterceptor###
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
我們抓住主干部分:
首先向服務(wù)器發(fā)送 request header和request body;
讀取 response header,先構(gòu)造一個(gè) Response 對(duì)象;
如果有 response body,就在 3 的基礎(chǔ)上加上 body 構(gòu)造一個(gè)新的 Response 對(duì)象;
這里我們可以看到,核心工作都由 HttpCodec 對(duì)象完成,而 HttpCodec 實(shí)際上利用的是 Okio,而 Okio 實(shí)際上還是用的 Socket。
我們?cè)賮?lái)說(shuō)說(shuō)自定義Interceptor ,我們可以用自定義的Interceptor 來(lái)干什么?比如,像我們請(qǐng)求接口都要加上token或者一些其他的一些公共參數(shù),我們就可以利用自定義Interceptor 來(lái)實(shí)現(xiàn):
public class ParamInterceptor implements Interceptor {
private Context mContext;
public ParamInterceptor(Context context ) {
mContext = context;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request oldrequest = chain.request();
Request.Builder requestBuilder = oldrequest.newBuilder()
.addHeader("Content-Type", "application/json");
if (isLogin) {
requestBuilder.addHeader("TOKEN","登錄返回的token");
}
Request request = requestBuilder.build();
return chain.proceed(request);
} }
這樣就實(shí)現(xiàn)了添加公共參數(shù)的功能
其實(shí) Interceptor 的設(shè)計(jì)也是一種分層的思想,每個(gè) Interceptor 就是一層。為什么要套這么多層呢?分層的思想在 TCP/IP 協(xié)議中就體現(xiàn)得淋漓盡致,分層簡(jiǎn)化了每一層的邏輯,每層只需要關(guān)注自己的責(zé)任(單一原則思想也在此體現(xiàn)),而各層之間通過(guò)約定的接口/協(xié)議進(jìn)行合作(面向接口編程思想),共同完成復(fù)雜的任務(wù)。
總結(jié)
OkHttp 還有很多細(xì)節(jié)部分沒有在本文探討,但建立一個(gè)清晰的概覽非常重要。對(duì)整體有了清晰認(rèn)識(shí)之后,細(xì)節(jié)部分如有需要,再單獨(dú)深入將更加容易。下一篇文章我們接著介紹Retrofit。