學會了OkHttp3的用法後,咱們固然有必要來了解下OkHttp3的源碼,固然如今網上的文章不少,我仍舊但願我這一系列文章篇是最簡潔易懂的。html
首先OKHttp3如何使用這裏就不在贅述了,不明白的同窗能夠查看Android網絡編程(五)OkHttp2.x用法全解析、
Android網絡編程(六)OkHttp3用法全解析這兩篇文章。當咱們要請求網絡的時候咱們須要用OkHttpClient.newCall(request)進行execute或者enqueue操做,當咱們調用newCall時:java
@Override public Call newCall(Request request) { return new RealCall(this, request); }
實際返回的是一個RealCall類,咱們調用enqueue異步請求網絡其實是調用了RealCall的enqueue方法:android
void enqueue(Callback responseCallback, boolean forWebSocket) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket)); }
能夠看到最終的請求是dispatcher來完成的。git
Dispatcher主要用於控制併發的請求,它主要維護瞭如下變量:github
/** 最大併發請求數*/ private int maxRequests = 64; /** 每一個主機最大請求數*/ private int maxRequestsPerHost = 5; /** 消費者線程池 */ private ExecutorService executorService; /** 將要運行的異步請求隊列 */ private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /**正在運行的異步請求隊列 */ private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** 正在運行的同步請求隊列 */ 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; }
Dispatcher有兩個構造函數,可使用本身設定線程池,若是沒有設定線程池則會在請求網絡前本身建立線程池,這個線程池相似於CachedThreadPool比較適合執行大量的耗時比較少的任務。不瞭解線程池的同窗能夠查看Android多線程(一)線程池這篇文章。其中用到了SynchronousQueue,不瞭解它的同窗能夠查看Java併發編程(六)阻塞隊列這篇文章。算法
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
當正在運行的異步請求隊列中的數量小於64而且正在運行的請求主機數小於5時則把請求加載到runningAsyncCalls中並在線程池中執行,不然就再入到readyAsyncCalls中進行緩存等待。編程
線程池中傳進來的參數就是AsyncCall它是RealCall的內部類,內部也實現了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); } }
首先咱們來看看最後一行, 不管這個請求的結果如何都會執行client.dispatcher().finished(this);服務器
synchronized void finished(AsyncCall call) { if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!"); promoteCalls(); }
finished方法將這次請求從runningAsyncCalls移除後還執行了promoteCalls方法:網絡
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. } }
能夠看到最關鍵的點就是會從readyAsyncCalls取出下一個請求,並加入runningAsyncCalls中並交由線程池處理。好了讓咱們再回到上面的AsyncCall的execute方法,咱們會發getResponseWithInterceptorChain方法返回了Response,很明顯這是在請求網絡。
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket); return chain.proceed(originalRequest); }
getResponseWithInterceptorChain方法,建立了ApplicationInterceptorChain,它是一個攔截器鏈,這個類也是RealCall的內部類,接下來執行了它的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方法每次從攔截器列表中取出攔截器,當存在多個攔截器時都會在第七行阻塞,並等待下一個攔截器的調用返回。下面分別以 攔截器鏈中有1個、2個攔截器的場景加以模擬:
攔截器主要用來觀察,修改以及可能短路的請求輸出和響應的回來。一般狀況下攔截器用來添加,移除或者轉換請求或者響應的頭部信息。好比將域名替換爲ip地址,將請求頭中添加host屬性,也能夠添加咱們應用中的一些公共參數,好比設備id、版本號等等。 不瞭解攔截器的能夠查看Okhttp-wiki 之 Interceptors 攔截器這篇文章。
回到代碼上來,咱們看最後一行 return getResponse(request, forWebSocket),若是沒有更多的攔截器的話,就會執行網絡請求,來看看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方法比較長我省略了一些代碼,能夠看到建立了HttpEngine類而且調用HttpEngine的sendRequest方法和readResponse方法。
咱們先來看看sendRequest方法:
public void sendRequest() throws RequestException, RouteException, IOException { if (cacheStrategy != null) return; // Already sent. if (httpStream != null) throw new IllegalStateException(); //請求頭部添加 Request request = networkRequest(userRequest); //獲取client中的Cache,同時Cache在初始化的時候會去讀取緩存目錄中關於曾經請求過的全部信息。 InternalCache responseCache = Internal.instance.internalCache(client); //cacheCandidate爲上次與服務器交互緩存的Response Response cacheCandidate = responseCache != null ? responseCache.get(request) : null; long now = System.currentTimeMillis(); //建立CacheStrategy.Factory對象,進行緩存配置 cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get(); //網絡請求 networkRequest = cacheStrategy.networkRequest; //緩存的響應 cacheResponse = cacheStrategy.cacheResponse; if (responseCache != null) { //記錄當前請求是網絡發起仍是緩存發起 responseCache.trackResponse(cacheStrategy); } if (cacheCandidate != null && cacheResponse == null) { closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. } //不進行網絡請求而且緩存不存在或者過時則返回504錯誤 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; } // 不進行網絡請求,並且緩存可使用,直接返回緩存 if (networkRequest == null) { userResponse = cacheResponse.newBuilder() .request(userRequest) .priorResponse(stripBody(priorResponse)) .cacheResponse(stripBody(cacheResponse)) .build(); userResponse = unzip(userResponse); return; } //須要訪問網絡時 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()); } } }
上面的代碼顯然是在發送請求,可是最主要的是作了緩存的策略。cacheCandidate是上次與服務器交互緩存的Response,這裏的緩存都是基於Map,key是請求中url的md5,value是在文件中查詢到的緩存,頁面置換基於LRU算法,咱們如今只須要知道它是一個能夠讀取緩存Header的Response便可。根據cacheStrategy的處理獲得了networkRequest和cacheResponse這兩個值,根據這兩個值的數據是否爲null來進行進一步的處理,當networkRequest和cacheResponse都爲null的狀況也就是不進行網絡請求而且緩存不存在或者過時,這時候則返回504錯誤;當networkRequest 爲null時也就是不進行網絡請求,並且緩存可使用時則直接返回緩存;其餘的狀況則請求網絡。
接下來咱們查看readResponse方法:
public void readResponse() throws IOException { ...省略 else{ //讀取網絡響應 networkResponse = readNetworkResponse(); } //將響應頭部存入Cookie中 receiveHeaders(networkResponse.headers()); // If we have a cache response too, then we're doing a conditional get. if (cacheResponse != null) { //檢查緩存是否可用,若是可用。那麼就用當前緩存的Response,關閉網絡鏈接,釋放鏈接。 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)); } }
這個方法發起刷新請求頭部和請求體,解析HTTP響應頭部。若是有緩存而且可用則用緩存的數據並更新緩存,不然就用網絡請求返回的數據。
咱們再來看看validate(cacheResponse, networkResponse)方法是如何判斷緩存是否可用的:
private static boolean validate(Response cached, Response network) { //若是服務器返回304則緩存有效 if (network.code() == HTTP_NOT_MODIFIED) { return true; } //經過緩存和網絡請求響應中的Last-Modified來計算是不是最新數據,若是是則緩存有效 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; }
如緩存果過時或者強制放棄緩存,在此狀況下,緩存策略所有交給服務器判斷,客戶端只用發送條件get請求便可,若是緩存是有效的,則返回304 Not Modifiled,不然直接返回body。條件get請求有兩種方式一種是Last-Modified-Date,一種是 ETag。這裏採用了Last-Modified-Date,經過緩存和網絡請求響應中的Last-Modified來計算是不是最新數據,若是是則緩存有效。
最後咱們再回到RealCall的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行當發生IOException或者RouteException時會執行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); }
最後一行能夠看到就是從新建立了HttpEngine並返回,用來完成重連。
到這裏OkHttp請求網絡的流程基本上講完了,下面是關於OKHttp的請求流程圖:
參考資料:
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/