Okhttp特性
Okhttp是一個(gè)高效的,請(qǐng)求速度更快,更節(jié)省流量的http庫(kù)。擁有以下特性。
- 支持SPDY和http2,對(duì)同一服務(wù)器的所有請(qǐng)求共享同一個(gè)socket。
- 擁有自動(dòng)維護(hù)的socket連接池,減少握手次數(shù)。
- socket自動(dòng)選擇最好路線(xiàn),并支持自動(dòng)重連。
- 擁有隊(duì)列線(xiàn)程池,輕松寫(xiě)并發(fā)。
- 擁有Interceptors輕松處理請(qǐng)求與響應(yīng)(比如透明GZIP壓縮,LOGGING)。
- 無(wú)縫的支持GZIP來(lái)減少數(shù)據(jù)流量。
- 支持基于Headers的緩存策略,緩存響應(yīng)數(shù)據(jù)來(lái)減少重復(fù)的網(wǎng)絡(luò)請(qǐng)求。
- 支持服務(wù)器多IP重連,支持從常用的連接問(wèn)題中自動(dòng)恢復(fù),還處理了代理服務(wù)器問(wèn)題和SSL握手失敗問(wèn)題。
注:SPDY是什么?
SPDY(讀作“SPeeDY”)是Google開(kāi)發(fā)的基于TCP的傳輸層協(xié)議,用以最小化網(wǎng)絡(luò)延遲,提升網(wǎng)絡(luò)速度,優(yōu)化用戶(hù)的網(wǎng)絡(luò)使用體驗(yàn)。
SPDY并不是一種用于替代HTTP的協(xié)議,而是對(duì)HTTP協(xié)議的增強(qiáng)。新協(xié)議的功能包括數(shù)據(jù)流的多路復(fù)用、請(qǐng)求優(yōu)先級(jí)以及HTTP報(bào)頭壓縮。
谷歌表示,引入SPDY協(xié)議后,在實(shí)驗(yàn)室測(cè)試中頁(yè)面加載速度比原先快64%。
OkHttpClient分析版本
Okhttp3(3.2.0版本)
OkHttpClient的簡(jiǎn)單調(diào)用
Get請(qǐng)求
public void doGet(String url){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
Log.i("輸出:" + response.body().string());
}
Post請(qǐng)求
public void doPost(String url){
MediaType json = MediaType.parse("application/json; charset=utf-8")
RequestBody body = RequestBody.create(JSON, json);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
Response response = client.newCall(request).execute();
Log.i("輸出:" + response.body().string());
}
OkHttpClient介紹
OkHttpClient顧名思義是一個(gè)http請(qǐng)求的客戶(hù)端,實(shí)現(xiàn)了Call.Factory接口,提供newCall方法用于創(chuàng)建一個(gè)請(qǐng)求調(diào)用Call。
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
OkHttpClient包含很多模塊,包括Dispatcher(請(qǐng)求分發(fā)器),ProxySelector(代理服務(wù)器選擇),InternalCache(內(nèi)部緩存)等其他的模塊,由它對(duì)外提供這些模塊的訪問(wèn),是典型的外觀模式,同時(shí)OkHttpClient設(shè)計(jì)為建造者模式,提供Builder方便為不同模塊進(jìn)行配置。
Request,Response,Call,RealCall,AsynCall介紹
- Request作為請(qǐng)求信息的封裝,內(nèi)部包含了請(qǐng)求url,請(qǐng)求方法method,請(qǐng)求頭headers,請(qǐng)求體RequestBody,tag標(biāo)簽等。
- Response作為相應(yīng)信息的封裝,內(nèi)部包含對(duì)應(yīng)的請(qǐng)求信息request,http協(xié)議protocol,響應(yīng)碼code,響應(yīng)頭headers,響應(yīng)消息message,響應(yīng)體ResponseBody等。
- Call作為一個(gè)請(qǐng)求的接口,提供獲取對(duì)應(yīng)請(qǐng)求信息息request,執(zhí)行請(qǐng)求execute,異步請(qǐng)求入隊(duì)enqueue,取消請(qǐng)求cancel等方法的定義。
- RealCall是Call請(qǐng)求的具體實(shí)現(xiàn),實(shí)現(xiàn)了同步請(qǐng)求執(zhí)行,異步請(qǐng)求入隊(duì),請(qǐng)求取消等操作。同時(shí)內(nèi)部提供ApplicationInterceptorChain負(fù)責(zé)請(qǐng)求和響應(yīng)的攔截處理。
- AsynCall是一個(gè)Runnable異步請(qǐng)求任務(wù),分發(fā)器Dispatcher負(fù)責(zé)管理這些異步請(qǐng)求,在可請(qǐng)求數(shù)量滿(mǎn)足的情況下會(huì)交給線(xiàn)程池執(zhí)行它的execute方法。
Dispatcher請(qǐng)求調(diào)用分發(fā)器
Dispatcher是請(qǐng)求Call的分發(fā)器,用于管理請(qǐng)求的等待,執(zhí)行,取消。分為同步請(qǐng)求RealCall和異步請(qǐng)求AsyncCall的管理。
- 針對(duì)同步請(qǐng)求,用runningSyncCalls隊(duì)列記錄正在執(zhí)行的同步請(qǐng)求。
- 針對(duì)異步請(qǐng)求,用readyAsyncCalls(待執(zhí)行的異步請(qǐng)求隊(duì)列)和runningAsyncCalls(正在執(zhí)行的異步請(qǐng)求隊(duì)列)記錄異步請(qǐng)求。
- 內(nèi)部提供線(xiàn)程池,負(fù)責(zé)執(zhí)行異步請(qǐng)求,查看executorService()。
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;
}
這個(gè)線(xiàn)程池,我們看參數(shù)解析:
1. 參數(shù)0,代表核心線(xiàn)程的數(shù)量,即線(xiàn)程池中最少線(xiàn)程時(shí)的數(shù)量(當(dāng)所有線(xiàn)程都空閑時(shí)),為0的話(huà),說(shuō)明線(xiàn)程空閑時(shí),不保留任何線(xiàn)程,做到線(xiàn)程低占用。
2. 參數(shù)Integer.MAX_VALUE,代表最大線(xiàn)程數(shù),即線(xiàn)程池中最多線(xiàn)程時(shí)的數(shù)量,為Integer.MAX_VALUE的話(huà),說(shuō)明可以開(kāi)啟無(wú)限多的線(xiàn)程進(jìn)行工作,線(xiàn)程工作完了再關(guān)閉。
3. 參數(shù)60,和參數(shù)TimeUnit.SECONDS,表示當(dāng)線(xiàn)程池的數(shù)量比核心線(xiàn)程的數(shù)量大時(shí),等待60秒之后就會(huì)去關(guān)閉空閑線(xiàn)程,使總的線(xiàn)程數(shù)量不會(huì)大于核心線(xiàn)程數(shù)量。
4. 參數(shù)new SynchronousQueue<Runnable>(), 代表這個(gè)線(xiàn)程等待隊(duì)列是同步隊(duì)列,當(dāng)有一個(gè)線(xiàn)程進(jìn)來(lái)時(shí),就同時(shí)會(huì)有一個(gè)線(xiàn)程出去,也就是說(shuō)線(xiàn)程不會(huì)停留在其中,一進(jìn)入就立馬出去執(zhí)行,這種方式在高頻請(qǐng)求時(shí)是很合適的。
5. 參數(shù)Util.threadFactory("OkHttp Dispatcher", false),表示提供一個(gè)線(xiàn)程工廠,用于創(chuàng)建新的線(xiàn)程。
這個(gè)線(xiàn)程池的設(shè)計(jì),是需要時(shí)可以創(chuàng)建無(wú)限多的線(xiàn)程,不需要時(shí)不保留任何線(xiàn)程,空閑線(xiàn)程60秒后如果還是空閑就會(huì)回收,以保證高阻塞低占用的使用。
- 設(shè)置有maxRequests(最大異步請(qǐng)求的數(shù)量)和maxRequestsPerHost(單個(gè)服務(wù)器的最大異步請(qǐng)求數(shù)量),這是為了限制同時(shí)執(zhí)行的總的請(qǐng)求數(shù)量和針對(duì)同一個(gè)服務(wù)器訪問(wèn)的請(qǐng)求數(shù)量,如果有超過(guò)了這兩個(gè)的限制,就將異步請(qǐng)求AsyncCall添加到readyAsyncCalls(待執(zhí)行的異步請(qǐng)求隊(duì)列)去等待執(zhí)行。這兩個(gè)參數(shù)可以配置。
- enqueue方法,AsyncCall入隊(duì)操作
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//未超過(guò)限制,執(zhí)行加入到線(xiàn)程池執(zhí)行異步請(qǐng)求
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//超過(guò)限制,將行異步請(qǐng)求加入等待隊(duì)列,等待執(zhí)行
readyAsyncCalls.add(call);
}
}
- finished方法,標(biāo)記當(dāng)前AsyncCall已經(jīng)完成,這時(shí)會(huì)考慮從異步等待隊(duì)列取出請(qǐng)求去執(zhí)行。
//標(biāo)記請(qǐng)求完成
synchronized void finished(AsyncCall call) {
if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
promoteCalls();
}
//從異步等待隊(duì)列取出請(qǐng)求去執(zhí)行,同時(shí)記錄到正在執(zhí)行的異步隊(duì)列中
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
- cancelAll方法,取消所有請(qǐng)求,包括所有同步請(qǐng)求,所有正在執(zhí)行的異步請(qǐng)求,所有等待執(zhí)行的異步請(qǐng)求。
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
RealCall 真正執(zhí)行請(qǐng)求調(diào)用的地方
RealCall是真正執(zhí)行請(qǐng)求調(diào)用的入口,無(wú)論是同步請(qǐng)求的execute,還是異步請(qǐng)求的enqueue(由Dispatcher進(jìn)行分發(fā)),最終都會(huì)到RealCall的getResponseWithInterceptorChain。
getResponseWithInterceptorChain里創(chuàng)建了一個(gè)ApplicationInterceptorChain攔截鏈來(lái)處理當(dāng)前RealCall中的Request請(qǐng)求數(shù)據(jù)。接下來(lái)講講Okhttp中攔截器是這樣的一種運(yùn)行模式。
Interceptors攔截器原理
Interceptors攔截器采用了責(zé)任鏈模式一層一層的處理請(qǐng)求響應(yīng)信息。這里我們從同步請(qǐng)求去分析添加了HttpLoggingInterceptor日志打印攔截器的運(yùn)行原理。
final class RealCall implements Call {
//執(zhí)行請(qǐng)求,這里創(chuàng)建了ApplicationInterceptorChain攔截鏈,負(fù)責(zé)對(duì)所有攔截器進(jìn)行調(diào)用,調(diào)用proceed開(kāi)始處理攔截
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
class ApplicationInterceptorChain implements Interceptor.Chain {
private final int index;
private final Request request;
private final boolean forWebSocket;
ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
this.index = index;
this.request = request;
this.forWebSocket = forWebSocket;
}
@Override public Connection connection() {
return null;
}
@Override public Request request() {
return request;
}
//這里遍歷攔截器,通過(guò)index在攔截鏈中找到對(duì)應(yīng)的攔截器,然后調(diào)用intercept進(jìn)行攔截處理,返回加工后的Response響應(yīng)信息
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
//當(dāng)所有攔截器都遍歷處理后,開(kāi)始執(zhí)行真正請(qǐng)求,返回Response,這里才是真正產(chǎn)生Response響應(yīng)的地方。
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
}
看了以上代碼之后,我們知道是通過(guò)index一個(gè)一個(gè)遍歷攔截鏈的攔截器,去攔截處理,這是一個(gè)遞歸的過(guò)程,當(dāng)所有的攔截器處理了Request請(qǐng)求信息之后(也就是真正請(qǐng)求之前的預(yù)處理,比如打日志,修改請(qǐng)求頭什么的),才真正的交給網(wǎng)絡(luò)請(qǐng)求引擎去執(zhí)行請(qǐng)求,返回響應(yīng)信息,然后又按相反順序?qū)esponse響應(yīng)信息進(jìn)行攔截處理(也就是對(duì)響應(yīng)信息的再加工,比如打印響應(yīng)信息等)。那么我們分析HttpLoggingInterceptor日志打印攔截器會(huì)更加的的清晰這個(gè)過(guò)程。
public final class HttpLoggingInterceptor implements Interceptor {
//攔截鏈上的
@Override public Response intercept(Chain chain) throws IOException {
Level level = this.level;
//獲取請(qǐng)求信息
Request request = chain.request();
//如果不打印日志,那就直接調(diào)用chain.proceed執(zhí)行下一個(gè)攔截操作,不做其他操作。
if (level == Level.NONE) {
return chain.proceed(request);
}
//這部分是打印請(qǐng)求信息,對(duì)請(qǐng)求進(jìn)行預(yù)處理(這里可以對(duì)請(qǐng)求信息進(jìn)行修改或做其他操作)
---------------------------------------
boolean logBody = level == Level.BODY;
boolean logHeaders = logBody || level == Level.HEADERS;
RequestBody requestBody = request.body();
boolean hasRequestBody = requestBody != null;
Connection connection = chain.connection();
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;
if (!logHeaders && hasRequestBody) {
requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
}
logger.log(requestStartMessage);
if (logHeaders) {
if (hasRequestBody) {
// Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
if (requestBody.contentType() != null) {
logger.log("Content-Type: " + requestBody.contentType());
}
if (requestBody.contentLength() != -1) {
logger.log("Content-Length: " + requestBody.contentLength());
}
}
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
// Skip headers from the request body as they are explicitly logged above.
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
logger.log(name + ": " + headers.value(i));
}
}
if (!logBody || !hasRequestBody) {
logger.log("--> END " + request.method());
} else if (bodyEncoded(request.headers())) {
logger.log("--> END " + request.method() + " (encoded body omitted)");
} else {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
logger.log("");
logger.log(buffer.readString(charset));
logger.log("--> END " + request.method()
+ " (" + requestBody.contentLength() + "-byte body)");
}
}
//請(qǐng)求預(yù)處理完成
----------------------------------
//這里調(diào)用chain.proceed執(zhí)行下一個(gè)攔截操作,返回Response響應(yīng)信息,結(jié)合ApplicationInterceptorChain的proceed方法,很容易看出是一個(gè)責(zé)任鏈的遞歸調(diào)用模式
long startNs = System.nanoTime();
Response response = chain.proceed(request);
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
//接下來(lái)打印響應(yīng)信息,對(duì)Response響應(yīng)進(jìn)行再加工(這里可以對(duì)響應(yīng)信息進(jìn)行修改或做其他操作)
---------------------------------------
ResponseBody responseBody = response.body();
long contentLength = responseBody.contentLength();
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
logger.log("<-- " + response.code() + ' ' + response.message() + ' '
+ response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "
+ bodySize + " body" : "") + ')');
if (logHeaders) {
Headers headers = response.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
logger.log(headers.name(i) + ": " + headers.value(i));
}
if (!logBody || !HttpEngine.hasBody(response)) {
logger.log("<-- END HTTP");
} else if (bodyEncoded(response.headers())) {
logger.log("<-- END HTTP (encoded body omitted)");
} else {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
logger.log("");
logger.log("Couldn't decode the response body; charset is likely malformed.");
logger.log("<-- END HTTP");
return response;
}
}
if (contentLength != 0) {
logger.log("");
logger.log(buffer.clone().readString(charset));
}
logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
}
}
//響應(yīng)再加工完成
----------------------------------
//這里返回響應(yīng)信息
return response;
}
}
明白攔截器的運(yùn)行模式之后,我們知道真正的請(qǐng)求是在RealCall的getResponse方法中開(kāi)始的。
/**
* Performs the request and returns the response. May return null if this call was canceled.
*/
Response getResponse(Request request, boolean forWebSocket) throws IOException {
//這里負(fù)責(zé)執(zhí)行請(qǐng)求,然后返回響應(yīng)數(shù)據(jù)
...
}
具體請(qǐng)求我們下節(jié)分析。