摘要:異步請(qǐng)求當(dāng)正在運(yùn)行的異步請(qǐng)求隊(duì)列中的數(shù)量小于并且正在運(yùn)行的請(qǐng)求主機(jī)數(shù)小于時(shí)則把請(qǐng)求加載到中并在線(xiàn)程池中執(zhí)行,否則就再入到中進(jìn)行緩存等待。通常情況下攔截器用來(lái)添加,移除或者轉(zhuǎn)換請(qǐng)求或者響應(yīng)的頭部信息。
前言
學(xué)會(huì)了OkHttp3的用法后,我們當(dāng)然有必要來(lái)了解下OkHttp3的源碼,當(dāng)然現(xiàn)在網(wǎng)上的文章很多,我仍舊希望我這一系列文章篇是最簡(jiǎn)潔易懂的。
1.從請(qǐng)求處理開(kāi)始分析首先OKHttp3如何使用這里就不在贅述了,不明白的同學(xué)可以查看Android網(wǎng)絡(luò)編程(五)OkHttp2.x用法全解析、
Android網(wǎng)絡(luò)編程(六)OkHttp3用法全解析這兩篇文章。當(dāng)我們要請(qǐng)求網(wǎng)絡(luò)的時(shí)候我們需要用OkHttpClient.newCall(request)進(jìn)行execute或者enqueue操作,當(dāng)我們調(diào)用newCall時(shí):
@Override public Call newCall(Request request) { return new RealCall(this, request); }
實(shí)際返回的是一個(gè)RealCall類(lèi),我們調(diào)用enqueue異步請(qǐng)求網(wǎng)絡(luò)實(shí)際上是調(diào)用了RealCall的enqueue方法:
void enqueue(Callback responseCallback, boolean forWebSocket) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket)); }
可以看到最終的請(qǐng)求是dispatcher來(lái)完成的。
2.Dispatcher任務(wù)調(diào)度 主要的變量Dispatcher主要用于控制并發(fā)的請(qǐng)求,它主要維護(hù)了以下變量:
/** 最大并發(fā)請(qǐng)求數(shù)*/ private int maxRequests = 64; /** 每個(gè)主機(jī)最大請(qǐng)求數(shù)*/ private int maxRequestsPerHost = 5; /** 消費(fèi)者線(xiàn)程池 */ private ExecutorService executorService; /** 將要運(yùn)行的異步請(qǐng)求隊(duì)列 */ private final Deque構(gòu)造函數(shù)readyAsyncCalls = new ArrayDeque<>(); /**正在運(yùn)行的異步請(qǐng)求隊(duì)列 */ private final Deque runningAsyncCalls = new ArrayDeque<>(); /** 正在運(yùn)行的同步請(qǐng)求隊(duì)列 */ private final Deque 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(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
Dispatcher有兩個(gè)構(gòu)造函數(shù),可以使用自己設(shè)定線(xiàn)程池,如果沒(méi)有設(shè)定線(xiàn)程池則會(huì)在請(qǐng)求網(wǎng)絡(luò)前自己創(chuàng)建線(xiàn)程池,這個(gè)線(xiàn)程池類(lèi)似于CachedThreadPool比較適合執(zhí)行大量的耗時(shí)比較少的任務(wù)。不了解線(xiàn)程池的同學(xué)可以查看Android多線(xiàn)程(一)線(xiàn)程池這篇文章。其中用到了SynchronousQueue,不了解它的同學(xué)可以查看Java并發(fā)編程(六)阻塞隊(duì)列這篇文章。
異步請(qǐng)求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)行的異步請(qǐng)求隊(duì)列中的數(shù)量小于64并且正在運(yùn)行的請(qǐng)求主機(jī)數(shù)小于5時(shí)則把請(qǐng)求加載到runningAsyncCalls中并在線(xiàn)程池中執(zhí)行,否則就再入到readyAsyncCalls中進(jìn)行緩存等待。
AsyncCall線(xiàn)程池中傳進(jìn)來(lái)的參數(shù)就是AsyncCall它是RealCall的內(nèi)部類(lèi),內(nèi)部也實(shí)現(xiàn)了execute方法:
@Override protected void execute() { boolean signalledCallback = false; try { Response response = getResponseWithInterceptorChain(forWebSocket); if (canceled) { 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! logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e); } else { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } }
首先我們來(lái)看看最后一行, 無(wú)論這個(gè)請(qǐng)求的結(jié)果如何都會(huì)執(zhí)行client.dispatcher().finished(this);
synchronized void finished(AsyncCall call) { if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn"t running!"); promoteCalls(); }
finished方法將此次請(qǐng)求從runningAsyncCalls移除后還執(zhí)行了promoteCalls方法:
private void promoteCalls() { if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. for (Iteratori = 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. } }
可以看到最關(guān)鍵的點(diǎn)就是會(huì)從readyAsyncCalls取出下一個(gè)請(qǐng)求,并加入runningAsyncCalls中并交由線(xiàn)程池處理。好了讓我們?cè)倩氐缴厦娴腁syncCall的execute方法,我們會(huì)發(fā)getResponseWithInterceptorChain方法返回了Response,很明顯這是在請(qǐng)求網(wǎng)絡(luò)。
3.Interceptor攔截器private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket); return chain.proceed(originalRequest); }
getResponseWithInterceptorChain方法,創(chuàng)建了ApplicationInterceptorChain,它是一個(gè)攔截器鏈,這個(gè)類(lèi)也是RealCall的內(nèi)部類(lèi),接下來(lái)執(zhí)行了它的proceed方法:
@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; } // No more interceptors. Do HTTP. return getResponse(request, forWebSocket); }
proceed方法每次從攔截器列表中取出攔截器,當(dāng)存在多個(gè)攔截器時(shí)都會(huì)在第七行阻塞,并等待下一個(gè)攔截器的調(diào)用返回。下面分別以 攔截器鏈中有1個(gè)、2個(gè)攔截器的場(chǎng)景加以模擬:
攔截器主要用來(lái)觀(guān)察,修改以及可能短路的請(qǐng)求輸出和響應(yīng)的回來(lái)。通常情況下攔截器用來(lái)添加,移除或者轉(zhuǎn)換請(qǐng)求或者響應(yīng)的頭部信息。比如將域名替換為ip地址,將請(qǐng)求頭中添加host屬性,也可以添加我們應(yīng)用中的一些公共參數(shù),比如設(shè)備id、版本號(hào)等等。 不了解攔截器的可以查看Okhttp-wiki 之 Interceptors 攔截器這篇文章。
回到代碼上來(lái),我們看最后一行 return getResponse(request, forWebSocket),如果沒(méi)有更多的攔截器的話(huà),就會(huì)執(zhí)行網(wǎng)絡(luò)請(qǐng)求,來(lái)看看getResponse方法做了些什么(RealCall.java):
Response getResponse(Request request, boolean forWebSocket) throws IOException { ...省略 // Create the initial HTTP engine. Retries and redirects need new engine for each attempt. engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null); int followUpCount = 0; while (true) { if (canceled) { engine.releaseStreamAllocation(); throw new IOException("Canceled"); } boolean releaseConnection = true; try { engine.sendRequest(); engine.readResponse(); releaseConnection = false; } catch (RequestException e) { // The attempt to interpret the request failed. Give up. throw e.getCause(); } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. ...省略 } }
getResponse方法比較長(zhǎng)我省略了一些代碼,可以看到創(chuàng)建了HttpEngine類(lèi)并且調(diào)用HttpEngine的sendRequest方法和readResponse方法。
4.緩存策略我們先來(lái)看看sendRequest方法:
public void sendRequest() throws RequestException, RouteException, IOException { if (cacheStrategy != null) return; // Already sent. if (httpStream != null) throw new IllegalStateException(); //請(qǐng)求頭部添加 Request request = networkRequest(userRequest); //獲取client中的Cache,同時(shí)Cache在初始化的時(shí)候會(huì)去讀取緩存目錄中關(guān)于曾經(jīng)請(qǐng)求過(guò)的所有信息。 InternalCache responseCache = Internal.instance.internalCache(client); //cacheCandidate為上次與服務(wù)器交互緩存的Response Response cacheCandidate = responseCache != null ? responseCache.get(request) : null; long now = System.currentTimeMillis(); //創(chuàng)建CacheStrategy.Factory對(duì)象,進(jìn)行緩存配置 cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get(); //網(wǎng)絡(luò)請(qǐng)求 networkRequest = cacheStrategy.networkRequest; //緩存的響應(yīng) cacheResponse = cacheStrategy.cacheResponse; if (responseCache != null) { //記錄當(dāng)前請(qǐng)求是網(wǎng)絡(luò)發(fā)起還是緩存發(fā)起 responseCache.trackResponse(cacheStrategy); } if (cacheCandidate != null && cacheResponse == null) { closeQuietly(cacheCandidate.body()); // The cache candidate wasn"t applicable. Close it. } //不進(jìn)行網(wǎng)絡(luò)請(qǐng)求并且緩存不存在或者過(guò)期則返回504錯(cuò)誤 if (networkRequest == null && cacheResponse == null) { userResponse = new Response.Builder() .request(userRequest) .priorResponse(stripBody(priorResponse)) .protocol(Protocol.HTTP_1_1) .code(504) .message("Unsatisfiable Request (only-if-cached)") .body(EMPTY_BODY) .build(); return; } // 不進(jìn)行網(wǎng)絡(luò)請(qǐng)求,而且緩存可以使用,直接返回緩存 if (networkRequest == null) { userResponse = cacheResponse.newBuilder() .request(userRequest) .priorResponse(stripBody(priorResponse)) .cacheResponse(stripBody(cacheResponse)) .build(); userResponse = unzip(userResponse); return; } //需要訪(fǎng)問(wèn)網(wǎng)絡(luò)時(shí) boolean success = false; try { httpStream = connect(); httpStream.setHttpEngine(this); if (writeRequestHeadersEagerly()) { long contentLength = OkHeaders.contentLength(request); if (bufferRequestBody) { if (contentLength > Integer.MAX_VALUE) { throw new IllegalStateException("Use setFixedLengthStreamingMode() or " + "setChunkedStreamingMode() for requests larger than 2 GiB."); } if (contentLength != -1) { // Buffer a request body of a known length. httpStream.writeRequestHeaders(networkRequest); requestBodyOut = new RetryableSink((int) contentLength); } else { // Buffer a request body of an unknown length. Don"t write request headers until the // entire body is ready; otherwise we can"t set the Content-Length header correctly. requestBodyOut = new RetryableSink(); } } else { httpStream.writeRequestHeaders(networkRequest); requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength); } } success = true; } finally { // If we"re crashing on I/O or otherwise, don"t leak the cache body. if (!success && cacheCandidate != null) { closeQuietly(cacheCandidate.body()); } } }
上面的代碼顯然是在發(fā)送請(qǐng)求,但是最主要的是做了緩存的策略。cacheCandidate是上次與服務(wù)器交互緩存的Response,這里的緩存都是基于Map,key是請(qǐng)求中url的md5,value是在文件中查詢(xún)到的緩存,頁(yè)面置換基于LRU算法,我們現(xiàn)在只需要知道它是一個(gè)可以讀取緩存Header的Response即可。根據(jù)cacheStrategy的處理得到了networkRequest和cacheResponse這兩個(gè)值,根據(jù)這兩個(gè)值的數(shù)據(jù)是否為null來(lái)進(jìn)行進(jìn)一步的處理,當(dāng)networkRequest和cacheResponse都為null的情況也就是不進(jìn)行網(wǎng)絡(luò)請(qǐng)求并且緩存不存在或者過(guò)期,這時(shí)候則返回504錯(cuò)誤;當(dāng)networkRequest 為null時(shí)也就是不進(jìn)行網(wǎng)絡(luò)請(qǐng)求,而且緩存可以使用時(shí)則直接返回緩存;其他的情況則請(qǐng)求網(wǎng)絡(luò)。
接下來(lái)我們查看readResponse方法:
public void readResponse() throws IOException { ...省略 else{ //讀取網(wǎng)絡(luò)響應(yīng) networkResponse = readNetworkResponse(); } //將響應(yīng)頭部存入Cookie中 receiveHeaders(networkResponse.headers()); // If we have a cache response too, then we"re doing a conditional get. if (cacheResponse != null) { //檢查緩存是否可用,如果可用。那么就用當(dāng)前緩存的Response,關(guān)閉網(wǎng)絡(luò)連接,釋放連接。 if (validate(cacheResponse, networkResponse)) { userResponse = cacheResponse.newBuilder() .request(userRequest) .priorResponse(stripBody(priorResponse)) .headers(combine(cacheResponse.headers(), networkResponse.headers())) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); networkResponse.body().close(); releaseStreamAllocation(); // Update the cache after combining headers but before stripping the // Content-Encoding header (as performed by initContentStream()). InternalCache responseCache = Internal.instance.internalCache(client); responseCache.trackConditionalCacheHit(); // 更新緩存 responseCache.update(cacheResponse, stripBody(userResponse)); userResponse = unzip(userResponse); return; } else { closeQuietly(cacheResponse.body()); } } userResponse = networkResponse.newBuilder() .request(userRequest) .priorResponse(stripBody(priorResponse)) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); if (hasBody(userResponse)) { maybeCache(); userResponse = unzip(cacheWritingResponse(storeRequest, userResponse)); } }
這個(gè)方法發(fā)起刷新請(qǐng)求頭部和請(qǐng)求體,解析HTTP響應(yīng)頭部。如果有緩存并且可用則用緩存的數(shù)據(jù)并更新緩存,否則就用網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù)。
我們?cè)賮?lái)看看validate(cacheResponse, networkResponse)方法是如何判斷緩存是否可用的:
private static boolean validate(Response cached, Response network) { //如果服務(wù)器返回304則緩存有效 if (network.code() == HTTP_NOT_MODIFIED) { return true; } //通過(guò)緩存和網(wǎng)絡(luò)請(qǐng)求響應(yīng)中的Last-Modified來(lái)計(jì)算是否是最新數(shù)據(jù),如果是則緩存有效 Date lastModified = cached.headers().getDate("Last-Modified"); if (lastModified != null) { Date networkLastModified = network.headers().getDate("Last-Modified"); if (networkLastModified != null && networkLastModified.getTime() < lastModified.getTime()) { return true; } } return false; }
如緩存果過(guò)期或者強(qiáng)制放棄緩存,在此情況下,緩存策略全部交給服務(wù)器判斷,客戶(hù)端只用發(fā)送條件get請(qǐng)求即可,如果緩存是有效的,則返回304 Not Modifiled,否則直接返回body。條件get請(qǐng)求有兩種方式一種是Last-Modified-Date,一種是 ETag。這里采用了Last-Modified-Date,通過(guò)緩存和網(wǎng)絡(luò)請(qǐng)求響應(yīng)中的Last-Modified來(lái)計(jì)算是否是最新數(shù)據(jù),如果是則緩存有效。
5.失敗重連最后我們?cè)倩氐絉ealCall的getResponse方法:
Response getResponse(Request request, boolean forWebSocket) throws IOException { ...省略 boolean releaseConnection = true; try { engine.sendRequest(); engine.readResponse(); releaseConnection = false; } catch (RequestException e) { // The attempt to interpret the request failed. Give up. throw e.getCause(); } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null); if (retryEngine != null) { releaseConnection = false; engine = retryEngine; continue; } // Give up; recovery is not possible. throw e.getLastConnectException(); } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. HttpEngine retryEngine = engine.recover(e, null); if (retryEngine != null) { releaseConnection = false; engine = retryEngine; continue; } // Give up; recovery is not possible. throw e; } finally { // We"re throwing an unchecked exception. Release any resources. if (releaseConnection) { StreamAllocation streamAllocation = engine.close(); streamAllocation.release(); } } ...省略 engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null, response); } }
查看代碼第11行和21行當(dāng)發(fā)生IOException或者RouteException時(shí)會(huì)執(zhí)行HttpEngine的recover方法:
public HttpEngine recover(IOException e, Sink requestBodyOut) { if (!streamAllocation.recover(e, requestBodyOut)) { return null; } if (!client.retryOnConnectionFailure()) { return null; } StreamAllocation streamAllocation = close(); // For failure recovery, use the same route selector with a new connection. return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody, forWebSocket, streamAllocation, (RetryableSink) requestBodyOut, priorResponse); }
最后一行可以看到就是重新創(chuàng)建了HttpEngine并返回,用來(lái)完成重連。
到這里OkHttp請(qǐng)求網(wǎng)絡(luò)的流程基本上講完了,下面是關(guān)于OKHttp的請(qǐng)求流程圖:
參考資料:
http://www.jianshu.com/p/aad5aacd79bf
http://www.jianshu.com/p/64e256c1dbbf
http://www.cnblogs.com/LuLei1990/p/5534791.html
http://frodoking.github.io/2015/03/12/android-okhttp/
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70587.html
摘要:創(chuàng)建增加返回值為的支持這里的加上之前定義的參數(shù)形成完整的請(qǐng)求地址用于指定返回的參數(shù)數(shù)據(jù)類(lèi)型,這里我們支持和類(lèi)型。完整的代碼如下增加返回值為的支持請(qǐng)求參數(shù)上文講了訪(fǎng)問(wèn)網(wǎng)絡(luò)的基本方法,接下來(lái)我們來(lái)了解下常用的請(qǐng)求參數(shù)。 前言 Retrofit是Square公司開(kāi)發(fā)的一款針對(duì)Android網(wǎng)絡(luò)請(qǐng)求的框架,Retrofit2底層基于OkHttp實(shí)現(xiàn)的,而OkHttp現(xiàn)在已經(jīng)得到Google官方...
摘要:使用前準(zhǔn)備配置添加網(wǎng)絡(luò)權(quán)限異步請(qǐng)求慣例,請(qǐng)求百度可以省略,默認(rèn)是請(qǐng)求請(qǐng)求成功與版本并沒(méi)有什么不同,比較郁悶的是回調(diào)仍然不在線(xiàn)程。 前言 上一篇介紹了OkHttp2.x的用法,這一篇文章我們來(lái)對(duì)照OkHttp2.x版本來(lái)看看,OkHttp3使用起來(lái)有那些變化。當(dāng)然,看這篇文章前建議看一下前一篇文章Android網(wǎng)絡(luò)編程(五)OkHttp2.x用法全解析。 1.使用前準(zhǔn)備 Android ...
摘要:需要注意的是回調(diào)并不是在線(xiàn)程。也可以通過(guò)來(lái)同時(shí)取消多個(gè)請(qǐng)求。在開(kāi)始創(chuàng)建的時(shí)候配置好,在請(qǐng)求網(wǎng)絡(luò)的時(shí)候用將請(qǐng)求的結(jié)果回調(diào)給線(xiàn)程。最后調(diào)用這個(gè)的方法請(qǐng)求成功使用起來(lái)簡(jiǎn)單多了,而且請(qǐng)求結(jié)果回調(diào)是在線(xiàn)程的。 前言 講完了Volley,我們接下來(lái)看看目前比較火的網(wǎng)絡(luò)框架OkHttp, 它處理了很多網(wǎng)絡(luò)疑難雜癥:會(huì)從很多常用的連接問(wèn)題中自動(dòng)恢復(fù)。如果您的服務(wù)器配置了多個(gè)IP地址,當(dāng)?shù)谝粋€(gè)IP連接失...
閱讀 3947·2021-11-16 11:44
閱讀 3128·2021-11-12 10:36
閱讀 3384·2021-10-08 10:04
閱讀 1270·2021-09-03 10:29
閱讀 409·2019-08-30 13:50
閱讀 2623·2019-08-29 17:14
閱讀 1745·2019-08-29 15:32
閱讀 1090·2019-08-29 11:27