今天來拆解一下okhttp;分析一下okhttp如何做到這麼好;以及我們需要掌握什麼樣的技能或者思路去實現一個自己的“okhttp”;那先從入口開始看;
1.OkHttpClient ;先看頭部信息下面備註了;
//Cloneable 淺copy ,Call.Factory實現工廠接口(針對於普通接口)WebSocket.Factory實現工廠接口(針對websocket請求的接口)
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
}
a、我們現在先搞明白上面實現接口的目的;第一個Cloneable這個是淺copy使用的;暫時真的沒感覺OkHttpClient能在什麼場合使用到;但是這個還是提供了;Call.Factory =>主要是實現RealCall這個類;這個類裏面是實現普通的網絡請求的方式;WebSocket.Factory =>主要實現RealWebSocket這個類;這個類裏面實現webscoket的請求方式;
b、看OkHttpClient的參數是什麼;下面都進行備註了;
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);//創建一個只讀的;不可以修改的list;設置默認的 http/2 ,http/1.1
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);//創建一個只讀的;不可以修改的list;TLS配置
static {
Internal.instance = new Internal() {//創建一些方法裏面實現一些需要做的方法;後續講
...
};
}
final Dispatcher dispatcher;//開非核心線程進行,異步耗時操作(網絡請求)
final Proxy proxy;//默認空值
final List<Protocol> protocols;//設置網絡類型
final List<ConnectionSpec> connectionSpecs;//設置TLS的
final List<Interceptor> interceptors;//攔截器,僅支持初始化設置
final List<Interceptor> networkInterceptors;//網絡攔截器,僅支持初始化設置
final ProxySelector proxySelector;//設置安全的認證操作
final CookieJar cookieJar;//默認沒有cookies
final Cache cache;//緩存
final InternalCache internalCache;//網絡緩存
final SocketFactory socketFactory;//創建一個對象並將其連接到指定遠程端口上的指定遠程地址
// 對象還將綁定到提供的本地地址和端口。使用爲此工廠建立的對象選項配置對象。
//如果有安全管理器,checkConnect 則使用主機地址port 及其參數調用其方法。這可能會導致SecurityException。
final SSLSocketFactory sslSocketFactory;//設置保護HTTPS連接的工廠
final CertificateChainCleaner certificateChainCleaner;//設置限制哪些證書受信任的證書
final HostnameVerifier hostnameVerifier;//註冊認證的方式
final CertificatePinner certificatePinner;//反抓包
final Authenticator proxyAuthenticator;//返回一個請求,該請求包含滿足{@code response}中的身份驗證的憑據。如果無法滿足條件,則返回null,默認沒有
final Authenticator authenticator;//返回一個請求,該請求包含滿足{@code response}中的身份驗證的憑據。如果無法滿足條件,則返回null。默認沒有
final ConnectionPool connectionPool;//連接池 = 線程池 + 消息隊列
final Dns dns;//DNS配置
final boolean followSslRedirects;//是否允許Ssl重定向,默認true
final boolean followRedirects;//是否允許重定向,默認true
final boolean retryOnConnectionFailure;//是否連接失敗時重試,默認true
final int connectTimeout;//連接超時時間,默認10_000(10秒)
final int readTimeout;//讀取超時時間,默認10_000(10秒)
final int writeTimeout;//寫入超時時間,默認10_000(10秒)
final int pingInterval;//ping的時間間隔 默認 0
那麼我們從OkHttpClient裏面看到幾個重要點:看一下能夠調用的方法;發現到都是獲取配置;並沒有設置配置的接口;所以對應的OkHttpClient並沒有直接提供對應的設置;
所以OkhttpClient初始化OkHttpClient(Builder builder) 藉助於內部類Builder進行設置;
那麼看一下這個內部類的builder的參數
Dispatcher dispatcher;
Proxy proxy;
List<Protocol> protocols;//設置默認的 http/2 ,http/1.1
List<ConnectionSpec> connectionSpecs;//TLS配置
//下面和上面的okhttp一樣
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
初始化方法Builder()(這個是默認初始化的)和Builder(OkHttpClient okHttpClient)(可以將上次的okHttpClient設置的東西傳遞回去)
所以這個時候,先初始化一個builder類,再去重新設置該builder裏面的部分方法;或者參數
那麼我在builder裏面挑出幾個重要的點來講吧;
2、首先Dispatcher這個類很重要;首先這個類幹這幾件事情:
1、創建非核心線程池executorService -》爲之後的網絡請求做異步處理;
2、設置了readyAsyncCalls隊列(準備請求,開始網絡請求數大於閥值64,以及不能有相同正在請求的host數量超過5個),這個其實就是緩存暫時不請求的接口;在請求完畢之後留在重新觸發;
3、設置runningAsyncCalls隊列;異步請求的;
4、設置了runningSyncCalls隊列;同步請求的;
5、所以總的在請求數是runningAsyncCalls.size()+runningSyncCalls.size();
看一下圖;裏面的enqueue(AsyncCall call);添加進去做一個判斷(異步操作)
(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost)
runningAsyncCalls.add(call);//請求添加到隊列
executorService().execute(call);//開始進行異步請求
如果添加在準備隊列裏面,當請求結束的時候;回調該方法
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();//是否將ready裏面的值加到run裏面(異步)
runningCallsCount = runningCallsCount();//獲取請求的數量
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();//當無網絡請求之後都會進行一次回調
}
}
這樣的話,就完成了;一次網絡請求;這個講解的是異步以及是Dispatcher類;並不涉及真正的網絡請求之類的;
下面在說明一下同步請求的方式:
這個就涉及到RealCall裏面的execute()方法;會調用到.executed(this);然後添加進去
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
結束的時候也會調用finish的方法;這個就不涉及到是否進行最大值多少個之類的;來了就是請求;
我們現在來看一下請求的觸發點;當然我們直講重要的點RealCall;
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.internal.NamedRunnable;
import okhttp3.internal.cache.CacheInterceptor;
import okhttp3.internal.connection.ConnectInterceptor;
import okhttp3.internal.connection.StreamAllocation;
import okhttp3.internal.http.BridgeInterceptor;
import okhttp3.internal.http.CallServerInterceptor;
import okhttp3.internal.http.RealInterceptorChain;
import okhttp3.internal.http.RetryAndFollowUpInterceptor;
import okhttp3.internal.platform.Platform;
import static okhttp3.internal.platform.Platform.INFO;
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);
}
@Override public Request request() {
return originalRequest;
}
@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);
}
}
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
@Override public void enqueue(Callback responseCallback) {//異步請求的方式
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));//觸發異步請求;線程池
}
@Override public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
@Override public synchronized boolean isExecuted() {
return executed;
}
@Override public boolean isCanceled() {
return retryAndFollowUpInterceptor.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
@Override public RealCall clone() {
return new RealCall(client, originalRequest, forWebSocket);
}
StreamAllocation streamAllocation() {
return retryAndFollowUpInterceptor.streamAllocation();
}
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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);
}
}
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
String redactedUrl() {
return originalRequest.url().redact();
}
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);
}
}
我們分別看一下請求的方式(異步和同步)
1、enqueue(Callback responseCallback) --異步方式
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));//觸發runnable地方
}
final class AsyncCall extends NamedRunnable {
...
@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);
}
}
}
2、 Response 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);
}
}
所以重點是Response getResponseWithInterceptorChain()這個方法體;
我們現在去瞧瞧:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//先添加client提供的攔截器,build裏面才能添加;這個就是我們自定義的攔截器;這個是還沒有請求的攔截器
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);//FIRST 觸發
}
其中攔截器這個類是:
//攔截器都需要實現這個類
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;//其中proceed是觸發下面一個類執行的條件
Connection connection();
}
}
其中我們還需要看一下RealInterceptorChain這類:
@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();
calls++;
...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);//設置下一個攔截器的Chain,從0開始
...
return response;
}
那麼我們從FIRST開始看;觸發了proceed;那麼如果沒有添加我們的攔截器;直接觸發RetryAndFollowUpInterceptor攔截器;
public final class RetryAndFollowUpInterceptor implements Interceptor {
...
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);//複用和創建連接池的操作;這個在OkhttpClient裏面實現方法;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
...
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);//上面的代碼實現;將連接池設置進去
...
}
...
}
這個創建連接池之後開始proceed;那麼又調用RealInterceptorChain生成下一個攔截器的chain並且調用intercept
BridgeInterceptor填充cookie等,再次調用chain.proceed(requestBuilder.build());這個不需要在傳遞連接池;
然後在次觸發CacheInterceptor,一樣的再次觸發ConnectInterceptor,CallServerInterceptor,觸發之後在依次執行之前的proceed之後的方法;當然這個是繼續執行;最終回到RetryAndFollowUpInterceptor裏面執行followUpRequest然後返回response;
然後我們進行回調等處理;這樣不管是異步還是同步;走的方式都是責任鏈的;
之後會繼續講解一下里面實現的細節;比如如何創建scoket等等