如今大多數 Android 開發者都是使用的 OkHttp 框架進行網絡請求的,只不過使用的方式千差萬別,有的是在 OkHttp 之上進行封裝,有的藉助於 Retrofit(OkHttp 的一個封裝框架) 又進行了一層封裝,封裝的方式也各有不一樣,其實並無什麼好壞之分,適合本身的項目纔是最好的。css
不過無論使用哪一種方式使用 OkHttp,最好是能懂 OkHttp 的原理,理解爲何咱們能夠很方便的使用它。java
本文就是經過基本的同步和異步請求詳細解讀 OkHttp 原理的。web
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = client.newCall(request);
Response response = call.execute();
System.out.println(response.body().string());
}
複製代碼
因爲使用了 OkHttp,咱們使用上面很簡單的代碼,實現了一次網路請求,請求結果以下:緩存
一個同步的請求過程以下:安全
雖然上面經過 4 步就實現了一次網絡請求,實際上,OkHttp 替咱們作了不少事情,下面就依次來分析下:服務器
上面咱們是經過 Builder 模式構建的 OkHttpClient 對象,咱們知道,使用構建者模式,內部必然是有一個 Builder 類的,先看下 OkHttpClient 內的 Builder 類:cookie
public static final class Builder {
Dispatcher dispatcher;// OkHttp 內部分發類
Proxy proxy; // 代理
List<Protocol> protocols;// 應用層協議列表
List<ConnectionSpec> connectionSpecs;// 指定HTTP流量經過的套接字鏈接的配置列表
final List<Interceptor> interceptors = new ArrayList<>(); //用戶自定義攔截器集合
final List<Interceptor> networkInterceptors = new ArrayList<>();// 用戶自定義網絡攔截器集合
ProxySelector proxySelector;// 鏈接時,選擇的代理服務器
CookieJar cookieJar;// 爲HTTP Cookie 提供策略和持久性
Cache cache;// 用戶啓用緩存時設置的緩存類
InternalCache internalCache;// 內部緩存
SocketFactory socketFactory;// Http套接字工廠
SSLSocketFactory sslSocketFactory;// 使用 Https 時,ssl 套接字工廠類
CertificateChainCleaner certificateChainCleaner;// 驗證確認證書,
HostnameVerifier hostnameVerifier;// 驗證確認Https請求的主機名
CertificatePinner certificatePinner;//證書鎖定,用來約束信任的認證機構。
Authenticator proxyAuthenticator;// 代理身份驗證
Authenticator authenticator;// 身份驗證
ConnectionPool connectionPool;// 鏈接池
Dns dns;// 解析主機名的 ip 地址
boolean followSslRedirects;// 是否容許 ssl 重定向
boolean followRedirects;// 是否容許重定向
boolean retryOnConnectionFailure;// 是否在鏈接失敗時重試
int connectTimeout;// 鏈接超時時間
int readTimeout;// 讀取超時時間
int writeTimeout;// 寫入超時時間
int pingInterval;// ping 的間隔
}
複製代碼
在 構造方法時初始化一些屬性以下:網絡
public Builder() {
dispatcher = new Dispatcher();
// 協議版本 Protocol.HTTP_2, Protocol.HTTP_1_1
protocols = DEFAULT_PROTOCOLS;
//套接字列表ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT
connectionSpecs = DEFAULT_CONNECTION_SPECS;
//設置默認代理
proxySelector = ProxySelector.getDefault();
// 不使用 Cookie
cookieJar = CookieJar.NO_COOKIES;
// 使用默認套接字工廠
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTAN
certificatePinner = CertificatePinner.DEFAUL
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
//建立鏈接池
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
//容許 SSL 重定向
followSslRedirects = true;
//容許重定向
followRedirects = true;
// 鏈接失敗時會重連
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
複製代碼
上面的 Builder 類的屬性基本也是 OkHttpClient 類的一些屬性,在使用 Builder 模式時,在建立 Buidler 的時候,會初始化一些配置嗎,而後再build 方法中,返回一個 OkHttpClient 對象,app
最終構建 OkHttpClient :框架
public OkHttpClient build() {
return new OkHttpClient(this);
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
// 默認不是 TLS 的
boolean isTLS = false;
// 遍歷鏈接配置列表,看看有沒有 TLS 的,前面在 builder 的構造方法裏面初始化的三個。
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
// 若是設置了 ssl 套接字工廠,或者不是加密的,就使用默認的,也就是空
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
// 未設置,而且是 TLS 加密,使用默認的配置
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
複製代碼
這裏的 Request 一樣使用的是 Builder 構建者模式,主要存儲的信息有 :
public final class Request {
// 存儲了請求 URL相關的信息
final HttpUrl url;
// 請求方法,好比 GET、POST 等
final String method;
// 請求頭類
final Headers headers;
//請求體類,咱們在 POST 請求的時候,常常會用到,
final RequestBody body;
// 標記
final Object tag;
// 保證線程可見性的 緩存控制,是延遲初始化的
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
}
複製代碼
構造方法傳入了 Builder 類,
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
}
複製代碼
可見,在 Builder 類中,在建立 Builder 的無參構造方法中,默認是使用 「GET」請求的,同時建立了 Headers 類。 重點看下 咱們常常用的 url 方法:
public Builder url(String url) {
// url 不容許爲空
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
// 替換成 http 或者是 https
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
//建立 HttpUrl 對象
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
//調用 url(HttpUrl url)
return url(parsed);
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
複製代碼
經過上面能夠看到,咱們平時大多數都是傳入的 String,Okhttp 內部是會把咱們傳入的 String 構建成一個 HttpUrl 的對象再去使用的。
HttpUrl 內部包含的常見信息以下:
這裏不明白的,建議去看一下計算機網絡學習之 http 相關中講解的 URI 和 URL。
經過上面的過程構建出了一個 Request 對象。主要包含的信息其實也就是HTTP 的報文: 請求頭、請求體、請求地址、方法什麼的都有了,算是對 HTTP 請求報文的封裝了。
Call 是一個接口:
public interface Call extends Cloneable {
// 返回最初的 Request 對象
Request request();
// 同步請求
Response execute() throws IOException;
// 異步請求
void enqueue(Callback responseCallback);
//若是可能,取消請求,可是完成的請求不能取消
void cancel();
// 是否執行
boolean isExecuted();
// 是否取消
boolean isCanceled();
// copy 一個 Call
Call clone();
//接口建立 Call
interface Factory {
Call newCall(Request request);
}
}
複製代碼
Call 對象是根據 OkHttpClient 的 newCall 方法構建的,
@Override public Call newCall(Request request) {
// 建立 RealCall 對象,可見 RealCall 對象是 Call 的子類
return new RealCall(this, request, false /* for web socket */);
}
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
}
複製代碼
RealCall 實現了 Call 接口,是 OkHttp中實現具體請求的執行者,裏面持有 OkHttpClient 和 Request 對象。
Response response = call.execute();
複製代碼
在 2.4 中講了,call 實際上是 RealCall類的實例,因此調用的是 RealCall 中的 execute() 方法:
@Override public Response execute() throws IOException {
// 加synchronized 保證線程安全,也就保證了同時只能有一個線程訪問,不會發生 executed值的錯誤
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
// 執行的地方
try {
// 關鍵點 1 調用分發器類 Dispatcher 的 executed方法,並傳入當前 RealCall
client.dispatcher().executed(this);
// 關鍵點 2 使用攔截器鏈執行網絡請求
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
// 關鍵點 3 銷燬本次網絡請求
client.dispatcher().finished(this);
}
}
複製代碼
這裏對關鍵點 一、二、3 分別講解:
這裏的 Dispatcher 對象是在建立 OkHttpClient 的時候初始化的,若是咱們沒指定,就會建立一個默認的 Dispatcher 對象(前面建立 OkHttpClient 的時候代碼有),具體看下這個很是重要的分發器類:
這個類很重要,因此代碼所有貼出來
public final class Dispatcher {
//最大請求數
private int maxRequests = 64;
//一個請求地址的最大請求數
private int maxRequestsPerHost = 5;
// 沒有請求時的 callback
private Runnable idleCallback;
//延遲初始化的線程池,用來執行異步請求的請求
private ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
// 準備運行的雙端隊列,按照加入順序運行。
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() {
}
// 異步運行時調用,若是未建立線程池,則建立一個。這裏使用了 SynchronousQueue,這種隊列不會保存元素,在覈心線程滿的時候,而最大線程數有剩餘,直接建立新線程處理任務
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;
}
// 設置同時執行的最大請求書,
public synchronized void setMaxRequests(int maxRequests) {
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
this.maxRequests = maxRequests;
promoteCalls();
}
// 獲取最大請求數
public synchronized int getMaxRequests() {
return maxRequests;
}
//設置一個域名同時最大請求數
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
this.maxRequestsPerHost = maxRequestsPerHost;
promoteCalls();
}
public synchronized int getMaxRequestsPerHost() {
return maxRequestsPerHost;
}
//設置每次調度程序空閒時調用的回調(當運行的調用數返回到零時)
public synchronized void setIdleCallback(Runnable idleCallback) {
this.idleCallback = idleCallback;
}
// 異步請求調用
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
// 取消全部請求
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
// 主要是異步請求用,從異步準備隊列添加到異步運行隊列
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.
}
}
// 獲取傳入 call 的同域名的當前正在請求數
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}
// 同步請求調用
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
// 異步請求結束時調用
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
// 同步請求結束時調用
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();
}
}
// 返回當前正在等待的Calls 列表
public synchronized List<Call> queuedCalls() {
List<Call> result = new ArrayList<>();
for (AsyncCall asyncCall : readyAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
// 返回當前正在運行的 Calls 列表
public synchronized List<Call> runningCalls() {
List<Call> result = new ArrayList<>();
result.addAll(runningSyncCalls);
for (AsyncCall asyncCall : runningAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
// 返回當前正在等待的 Calls 大小
public synchronized int queuedCallsCount() {
return readyAsyncCalls.size();
}
// 返回當前正在運行的 Calls 大小
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
}
複製代碼
Dispatch 的代碼不是不少,但倒是很是重要的一個類,用來進行具體的網絡請求分發。
如今繼續看關鍵點 1,這裏執行了 Dispatch 的 executed 方法 。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
複製代碼
很簡單 ,直接把 Call 對象添加到同步請求隊列中,別的也沒作什麼操做。
Response result = getResponseWithInterceptorChain();
複製代碼
這裏就是 OkHttp 實現網絡請求最核心的地方,經過攔截器鏈實現請求。
Response getResponseWithInterceptorChain() throws IOException {
// 構建一整套鏈接器
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));
//若是不是 web 鏈接,添加用戶自定義網絡攔截器
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);
}
複製代碼
能夠看到這裏是經過攔截器鏈請求,並其中返回結果的。
其中 RealInterceptorChain 實現了 Interceptor.Chain 接口,代碼以下
public final class RealInterceptorChain implements Interceptor.Chain {
// 攔截器鏈
private final List<Interceptor> interceptors;
//
private final StreamAllocation streamAllocation;
// 對 Http 請求進行編碼,對 Http響應進行解碼
private final HttpCodec httpCodec;
// 鏈接
private final Connection connection;
// 當前索引
private final int index;
// 請求
private final Request request;
// 計數器,保證不能屢次執行,屢次執行會異常
private int calls;
// 構造函數
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
// 省略部分代碼。。。
// 這裏是核心的 proceed 方法,根據傳入的 request 以及構造時生成的信息,調用另一個 proceed 方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
// 具體處理
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection) throws IOException {
// 若是是當前的索引,大於總攔截器個數,拋出異常
if (index >= interceptors.size()) throw new AssertionError();
// 沒超過,就執行次數+1
calls++;
//確保一個攔截器鏈中的請求都是執行同個地址的
if (this.httpCodec != null && !sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// 確保一個攔截器鏈中的每一個攔截器只能執行一次
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 生成一個新的 RealInterceptorChain 對象,準備調用攔截器鏈中的下一個攔截器
// 注意這是是使用的 index+1 依次進行構建的,一直到 CallServerInterceptor,
// CallServerInterceptor中不會再調用
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
// 拿到當前的攔截器
Interceptor interceptor = interceptors.get(index);
// 使用當前攔截器的 intercept 調用下個攔截器
Response response = interceptor.intercept(next);
// 確認下個攔截器對 chain.proceed() 進行了調用
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// 確認返回值不爲 null
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
// 返回響應結果 Response 對象
return response;
}
// 判斷請求地址是夠相同
private boolean sameConnection(HttpUrl url) {
return url.host().equals(connection.route().address().url().host())
&& url.port() == connection.route().address().url().port();
}
}
複製代碼
上面對 RealInterceptorChain 進行了大概的介紹,RealInterceptorChain 的原理其實就是在其內部會依次構建新攔截器鏈並用當前攔截器調用下個攔截器。
流程以下:
// 這裏傳入index 傳入 0,表明 List 中第一個攔截器,而且把原始請求傳入
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
複製代碼
注意這裏傳入了三個 null,對應的分別是 StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection,
。
在這裏,依次調用下個攔截器的 intercept 方法去獲取 Response 結果,這裏先以 RetryAndFollowUpInterceptor 的 intercept 方法爲例,看看作了什麼 :
RetryAndFollowUpInterceptor 的 intercept 方法:
chain 是咱們傳入的下個攔截器鏈,能夠看到在 RetryAndFollowUpInterceptor 的 Intercept內部,會調用傳入的 chain 的 proceed 方法去獲取結果,這就又回到了上面的的 RealInterceptorChain 的 proceed 方法裏面,依次又調用下個攔截器去獲取結果。一直到最後一個 Intercept 方法調用結束,而後會依次向前返回 Response 結果。
在最後的一個攔截器 CallServerIntercept 中,不會再去調用 傳入的 Chain 對象,完成最終的數據交互,而後依次返還給上一級。
大體流程以下(先不考慮緩存等狀況):
是否是很棒的設計,使用了責任鏈模式很棒的完成了一次網絡請求的處理。
關於每一個攔截器的具體實現,後面的文章再去分析。
client.dispatcher().finished(this);
複製代碼
這行代碼是在 finally 代碼塊裏面的,意味着不論是請求成功或者失敗,都會執行到。
這裏調用了 Dispatcher 對象裏面的 finished 方法,並傳入了當前 Call 對象,其實這個方法前面再講 Dispatcher 的時候已經講過,再來看下
void finished(RealCall call) {
// 調用重載finished方法, 因爲是同步請求,傳入同步請求隊列,而後傳入當前 Call 對象,
//第三個參數是是否調用 promoteCalls() 函數控制的,promoteCalls() 是隻在 異步請求的時候調用,因此這裏傳入 false
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
// 線程同步
synchronized (this) {
// 從當前同步隊列移除 call,若是失敗,拋出異常
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
// 異步請求調用
if (promoteCalls) promoteCalls();
// 統計正在運行的同步和異步請求的個數
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
// 同步和異步請求的個數爲 0,也就是沒有請求了,而且idleCallback 不爲空,則運行idleCallback 處理一些事情
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
複製代碼
其實上面的也就是把完成的網絡請求進行移除,而後若是是所有網絡請求完成,則能夠經過idleCallback.run 執行一些操做。
經過以上就是一個完整的同步請求,從建立、請求、響應、完成、銷燬的過程,下面再來看下異步請求。
一個異步的請求過程以下:
這其中的前三步,和同步請求時同樣的,不同的就是第四步,具體來看下第四步:
參考同步請求
參考同步請求
參考同步請求
RealCall 的 enqueue 方法以下:
@Override public void enqueue(Callback responseCallback) {
// 線程同步,保證狀態安全
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
// 構建一個 AsyncCall 對象,並調用 Dispatcher 的 enqueue 方法:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製代碼
先看 Dispatcher 的 enqueue 方法, AsyncCall 暫且先放下:
synchronized void enqueue(AsyncCall call) {
// 當前請求數小於最大請求數,而且一個服務器的鏈接數小於最大同服務器鏈接數
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//符合條件就直接加入運行隊列,
runningAsyncCalls.add(call);
// 而後經過線程池執行
executorService().execute(call);
} else {
// 若是超過了最大限制,就添加到準備隊列中
readyAsyncCalls.add(call);
}
}
// 得到一個線程池
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;
}
複製代碼
這裏很明顯,使用了線程池的 execute方法,也就是說這個 call(AsyncCall) 是一個Runnable 對象。
final class AsyncCall extends NamedRunnable {}
public abstract class NamedRunnable implements Runnable {
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
}
複製代碼
果不其然,AsyncCall 繼承於 NamedRunnable,是一個 Runnable 對象,在 NamedRunnable 內部實現了 run 方法,而且在 run 內部調用抽象函數 execute() ,AsyncCall 只用實現 execute 方法就行了:
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 {
// 仍是調用同步請求的時候介紹的攔截器鏈去獲取結果,這裏再也不贅述。
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);
}
}
}
複製代碼
能夠看到,這裏和同步請求很相似,都是經過 getResponseWithInterceptorChain() 去完成網絡請求的,因此說攔截器鏈是 OkHttp 的核心,最終請求完成之後從正在運行隊列中移除請求。一個異步請求就完成了。
那麼這裏有個問題,前面咱們講了,在 Dispatcher 分發器類中,關於異步請求的有隊列,一個異步準備隊列、一個異步正在執行隊列,上面講的是正在執行隊列的過程,那麼準備隊列裏面何時可以獲得執行呢?
仍是要回到 finished 方法
client.dispatcher().finished(this);
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
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();
}
}
複製代碼
這裏和同步請你去不一樣的是promoteCalls 傳入了 true,那麼就會執行:
if (promoteCalls) 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
AsyncCall call = i.next();
// 若是同個服務器地址正在運行的請求數 小於同個最大服務器地址請求數
if (runningCallsForHost(call) < maxRequestsPerHost) {
// 從準備隊列移除
i.remove();
//添加到運行隊列
runningAsyncCalls.add(call);
// 執行請求
executorService().execute(call);
}
// 若是正在運行數大於最大請求數,結束循環
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
複製代碼
到這裏,一個完整的一步請求過程就算講完了。
這是 OkHttp 源碼分析的第一篇,主要講請求的流程,關於具體攔截器分析,請關注後續。
歡迎關注個人公衆號: