OkHttp 是一款用於 Android 和 Java 的網絡請求庫,也是目前 Android 中最火的一個網絡庫。OkHttp 有不少的優勢:java
以前寫過一篇 Retrofit 源碼解析,Retrofit 底層其實就是用的 OkHttp 去請求網絡。本文分析 OKHttp 的源碼,主要是針對一次網絡請求的基本流程,源碼基於 OKHttp-3.8.0web
下面是 OkHttp 的使用示例:segmentfault
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(url) .build(); // 同步 Response response = client.newCall(request).execute(); // 異步 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
首先是建立一個 OkHttpClient
對象,其實用過的應該知道能夠用 new OkHttpClient.Builder().build()
的方式來配置 OkHttpClient
的一些參數。有了 OkHttpClient
以後,下面是建立一個 Request
對象,這個對象也是經過 Builder 模式來生成,其中能夠配置一些與這條請求相關的參數,其中 url 是必不可少的。在發送請求的時候,須要生成一個 Call
對象,Call
表明了一個即將被執行的請求。若是是同步請求,調用 execute
方法。異步則調用 enqueue
,並設定一個回調對象 Callback
。緩存
下面就一步步分析發送一條網絡請求的基本流程。cookie
OkHttpClient 的建立採用了 Builder 模式,能夠配置 Interceptor、Cache 等。能夠設置的參數不少,其中部分參數以下:網絡
final Dispatcher dispatcher; // 請求的分發器 final @Nullable Proxy proxy; // 代理 final List<Protocol> protocols; // http協議 final List<ConnectionSpec> connectionSpecs; final List<Interceptor> interceptors; final List<Interceptor> networkInterceptors; final EventListener.Factory eventListenerFactory; final ProxySelector proxySelector; final CookieJar cookieJar; final @Nullable Cache cache;
Request 與 OkHttpClient 的建立相似,也是用了 Buidler 模式,可是其參數要少不少:併發
public final class Request { final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Object tag; ... }
參數的含義都很明確,即便 Http 協議的url、header、method 以及 body 部分。變量 tag
用於標識一條 Request
,可用於發送後取消這條請求。異步
client.newCall(request)
生成一個 Call 對象。Call 其實是一個接口,它封裝了 Request,而且用於發起實際的網絡請求。下面是 Call 的所有代碼:socket
public interface Call extends Cloneable { Request request(); Response execute() throws IOException; void enqueue(Callback responseCallback); void cancel(); boolean isExecuted(); boolean isCanceled(); Call clone(); interface Factory { Call newCall(Request request); } }
其中包含了與網絡請求相關的操做,包括髮起、取消等。看一下 OkHttpClient
是如何建立 Call
的:async
@Override public Call newCall(Request request) { return new RealCall(this, request, false /* for web socket */); }
從代碼能夠看到,其實是建立了一個 RealCall
對象,它也是 Call 的惟一一個實現類。
有了 RealCall
對象後,就能夠發起網絡請求了,能夠是同步請求(execute
)或者是異步請求(enqueue
)。異步請求涉及到 Dispatcher
,先從相對簡單的同步請求開始分析。
調用 RealCall#execute()
便是發起同步請求,代碼以下:
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } }
首先判斷這條請求是否是已經執行過,若是是則會拋出異常(一條請求只能執行一次,重複執行能夠調用 Call#clone()
)。接着執行了 client.dispatcher().executed(this)
,這行代碼是把當前的 Call 加入到 Dispatcher 的一個隊列中,這個暫時能夠忽略,後面會分析 Dispatcher。
下面一行 Response result = getResponseWithInterceptorChain()
是關鍵,在 getResponseWithInterceptorChain
中真正執行了網絡請求並得到 Response 並返回。(下一小節具體分析其中的邏輯)
最後在 finally 中調用 Dispatcher 的 finished
,從隊列中移除這條請求。
getResponseWithInterceptorChain
的代碼以下:
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); return chain.proceed(originalRequest); }
能夠看到,其中建立了一個 List 用於添加 Interceptor。首先添加的是 client 中的 interceptors,也就是在建立 OkHttpClient
對象時自定義的 interceptors,而後依次添加 retryAndFollowUpInterceptor
(重試及重定向)、BridgeInterceptor
(請求參數的添加)、CacheInterceptor
(緩存)、ConnectInterceptor
(開始鏈接)、用戶自定義的 networkinterceptors
及 CallServerInterceptor
(發送參數並讀取響應)。從這裏能夠知道,OkHttp 默認添加了好幾個 interceptor 用於完成不一樣的功能。
在研究各個 interceptor 以前,須要考慮一下如何讓這些攔截器一個接着一個的執行?繼續看上面的代碼,在添加了各類 interceptors 以後,建立了一個 RealInterceptorChain
對象。(它的構造函數須要的參數不少,而且這些參數涉及到鏈接池、請求數據的發送等。因爲這篇文章主要分析 OkHttp 的基本流程,因此暫時略過這部分)RealInterceptorChain
是接口 Chain
的實現類,Chain
是 鏈 的意思,其做用是把各個 Interceptor 串起來依次執行。在得到了 RealInterceptorChain
以後調用其 proceed
方法,看名字就能知道是讓 Request
請求繼續執行。
下面具體分析 RealInterceptorChain
,它有以下的成員變量:
private final List<Interceptor> interceptors; // 攔截器 private final StreamAllocation streamAllocation; // 流管理器 private final HttpCodec httpCodec; // http流,發送請求數據並讀取響應數據 private final RealConnection connection; // scoket的鏈接 private final int index; // 當前攔截器的索引 private final Request request; // 當前的請求 private int calls; // chain 的 proceed 調用次數的記錄
其中 streamAllocation
、httpCodec
和 connection
都與 socket 鏈接有關,後續文章再分析。看一下 proceed
方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; // If we already have a stream, confirm that the incoming request will use it. // 若是已經有了一個流,確保即將到來的 request 是用它 if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must retain the same host and port"); } // If we already have a stream, confirm that this is the only call to chain.proceed(). // 若是已經有了一個流,確保這是對 call 惟一的調用 if (this.httpCodec != null && calls > 1) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must call proceed() exactly once"); } // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); // (1) Interceptor interceptor = interceptors.get(index); // (2) Response response = interceptor.intercept(next); // (3) // Confirm that the next interceptor made its required call to chain.proceed(). if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) { throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once"); } // Confirm that the intercepted response isn't null. if (response == null) { throw new NullPointerException("interceptor " + interceptor + " returned null"); } return response; }
剛開始作了一些鏈接方面的判斷,須要關注的是標了(1)、(2)、(3)的幾行,主要作了如下操做:
RealInterceptorChain
,其中 index
加1用於標識當前的攔截器index
獲取當前的攔截器intercept
方法,並把上面生成的新的 RealInterceptorChain 對象 next
傳進去由以前的 getResponseWithInterceptorChain
方法能夠知道,當前 RealInterceptorChain
的 interceptors 的第一個是 RetryAndFollowUpInterceptor
,下面是其 intercept
的代碼:
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url()), callStackTrace); int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response = null; boolean releaseConnection = true; try { // 調用 chain 的 proceed response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { ... // 省略部分代碼,主要是錯誤重試以及重定向 } }
這個 Interceptor 主要用於出錯重試以及重定向的邏輯,其中省略了部分代碼。在這個方法當中要關注的是再次調用了 chain
的 proceed
方法,這裏的 chain
是以前新建立的 next
對象。至關於說經過調用 Chain#proceed()
將網絡請求推向下一個攔截器(proceed
中會獲取下一個 Interceptor 並調用其 intercept
方法),而且獲得 response 對象,而下一個攔截器也是相似的操做。因而,多個 interceptors 就經過這種方式串起來依次執行,而且前一個 Interceptor 能夠獲得後一個 Interceptor 執行後的 response 從而進行處理。
經過不一樣的 Interceptor,OkHttp 實現了不一樣的功能。各個 Inercept 職責分明又不會互相耦合,而且能夠很是方便的添加 Interceptor,這是 責任鏈 模式的體現,很是優雅的設計。如今能夠發現 OkHttp 中的攔截器的調用過程以下圖所示:
相比於同步請求,異步請求主要是增長了 Dispatcher 的處理。Dispatcher 是請求的分發器,它有一下的成員變量:
private int maxRequests = 64; // 最大鏈接數 private int maxRequestsPerHost = 5; // 單個 host 最大鏈接數 private @Nullable Runnable idleCallback; // 空閒時的回調 /** Executes calls. Created lazily. */ private @Nullable ExecutorService executorService; // 線程池 /** Ready async calls in the order they'll be run. */ // 準備執行的異步 Call 的隊列 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ // 正在執行的的異步 Call 的隊列 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ // 正在執行的同步 Call 的隊列 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
在 Dispatcher 中,默認支持的最大併發鏈接數是64,每一個 host 最多能夠有5個併發請求。
下面看一下線程池 executorService
的建立。線程池會在兩個地方建立,分別是 Dispatcher 的構造函數或者是 executorService
方法中(若是調用了默認的構造函數):
// 默認構造函數沒有建立 public Dispatcher() { } // 自定義線程池 public Dispatcher(ExecutorService executorService) { this.executorService = 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; }
Dispatcher 支持自定義的線程池,不然會默認建立一個。在生成 OkHttpClient
對象時,默認調用的是 Dispatcher 無參的構造方法。這個默認線程池經過 `new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false))` 建立,看上去相似於一個 CachedThreadPool,沒有常駐的 core 線程,空閒線程60秒後自動關閉。
每一個 Call 被添加到某一個隊列,若是是同步請求添加到 runningSyncCalls
中:
synchronized void executed(RealCall call) { runningSyncCalls.add(call); }
異步請求添加的邏輯以下:
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
具體步驟是:
runningAsyncCalls
而且提交到線程池執行readyAsyncCalls
等待後續執行須要注意的是異步請求的 Call 不是原始的 Call,而是被包裝爲 AsyncCall
:
final class AsyncCall extends NamedRunnable { private final Callback responseCallback; AsyncCall(Callback responseCallback) { super("OkHttp %s", redactedUrl()); this.responseCallback = responseCallback; } ... @Override protected void execute() { boolean signalledCallback = false; try { // 調用 getResponseWithInterceptorChain Response response = getResponseWithInterceptorChain(); if (retryAndFollowUpInterceptor.isCanceled()) { 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! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } } }
AsyncCall
繼承自 NamedRunnable
,它其實就是一個爲線程設置了名字的 Runnable,在其 Run 中調用 execute
,因此 AsyncCall
的主要邏輯都寫在 execute
中。能夠看到最終仍是調用了 getResponseWithInterceptorChain
方法,因此後續執行網絡請求的邏輯是同樣的。在得到 response 以後,就能夠調用 responseCallback
返回最終的信息。
在上面的代碼中,finally 裏面執行了 client.dispatcher().finished(this)
,在同步請求 RealCall#execute()
中也有相似的一行代碼。finished
的做用是讓 Dispatcher 從隊列中移除已完成的 Call,對於異步請求還會從 readyAsyncCalls
中取出等待中的請求提交給線程池。下面是具體代碼:
/** Used by {@code AsyncCall#run} to signal completion. */ void finished(AsyncCall call) { finished(runningAsyncCalls, call, true); } /** Used by {@code Call#execute} to signal completion. */ void finished(RealCall call) { finished(runningSyncCalls, call, false); } private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); // 異步請求會進入 if (promoteCalls) promoteCalls(); runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); } } 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(); // 找到一個等待隊列中的 Call,符合鏈接數要求時加入 runningAsyncCalls 並提交給線程池執行。 if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }
有兩個重載的 finished
方法均調用了另外一個 pirvate 的 finished
,區別在於這個 finished
的最後一個參數 promoteCalls
。對於同步請求(參數爲 RealCall
) promoteCalls
爲 false
,而異步請求(參數爲 AsyncCall
) promoteCalls
爲 true
。 pirvate 的 finished
主要是從隊列中移除 Call,異步請求會執行 promoteCalls
。promoteCalls
裏面主要是從 readyAsyncCalls
取出一個 Call,若是知足最大鏈接數的要求,則把這個 Call 加入 runningAsyncCalls
並提交給線程池執行。
經過 runningAsyncCalls
和 readyAsyncCalls
,Dispatcher 實現了異步請求的調度執行。這裏比較巧妙的方式是在 finally 中去執行 readyAsyncCalls
中的請求,避免了 wait/notity 的方式,避免了代碼的複雜性。
OkHttp 的基本執行流程以下圖所示:
主要是如下步驟:
OkHttpClient
調用 newCall
建立 RealCall
對象,Call
封裝了 Request
,表明一條即將執行的請求。RealCall
的 execute
或 enqueue
方法,將Call
加入 Dispatcher
的相應隊列中。最終,同步或異步請求都會調用 getResponseWithInterceptorChain
。getResponseWithInterceptorChain
中,OkHttp 添加用戶自定義以及默認的 inceptors,並用一個 Chain
管理並依次執行每一個 Interceptor。Chain#proceed()
將請求發送給下一級的 Inceptor,並能經過這個方法得到下一級 Interceptor 的 Response。因此上圖所示,Request 一級級地往下傳遞,而獲取了網絡的 Response 以後一級級地往上傳遞。OkHttp中一條網絡請求的基本流程就是這樣,下一篇文章介紹 OkHttp 如何創建鏈接:OkHttp 源碼解析(二):創建鏈接。
若是個人文章對您有幫助,不妨點個贊支持一下(^_^)