本來想看看源碼提高下自己的水平,以前也有看過某些框架的源碼,但是基本都是只看某段,這次打算好好的研究一遍。本來是想拿RecyclerView的源碼來開刀的,但是好像RecyclerView的代碼沒那么容易看懂,而且變量還賊多,還大量用了設計模式,講真,看起來還真覺得挺費勁的。既然前段時間寫了Http,那我就拿okhttp的代碼來看看好了。
一.Okhttp
Android 4.4之后,HttpURLConnection底層實現已被OkHttp替換,okhttp內部依賴okio,現在的最新版本應該還是OkHttp3。其實我并直接的用過OkHttp來做網絡請求的操作,我用的是Retrofit,基于OkHttp,其實差不多。
1.簡單調用
一個簡單的異步GET請求的話可以這樣寫
OkHttpClient mOkHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback());
2.okhttp的原理
我的總結就是獲取到請求Call,進行一系列的Interceptors鏈的操作,異步的時候會涉及到Dispather的操作。
在網上很好的找到了一張圖來說明這個過程(出自https://blog.piasy.com/2016/07/11/Understand-OkHttp/)
我覺得圖中最核心的三個部分是
二.淺談Okhttp執行流程
同步和異步在調用時的區別就是同步調用的是execute方法,異步調用的是enqueue方法。
1.OkHttpClient
這個類用了Builder模式
private 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;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} 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;
}
里面也是有大量的屬性,基本都是和整個請求過程中相關的參數,比如dns 啊,connectTimeout 啊這些,所以我把它看成是一個抽象整個請求過程的對象。而且它的注釋也說了
就是一個Call的工廠,Call是什么之后會講。
2.Request與Response
請求時會創建一個Request對象。這兩個東西其實不用說就知道一個代表請求,一個代表響應。
看看這個Request其實也是在內部用了Builder模式
記住這個請求有五個屬性,地址,方法(就是GET這些),tag表示的是標志,不知道是干啥的,好像是取消請求的時候會用到的。然后還有請求頭和請求體。
(1)headers請求頭
它有個參數,20個長度的列表,保存的是請求頭相關的參數
比如addHeader方法,會調用Headers的add方法。
這個checkNameAndValue是判斷key和value是否符合,先不用管。
然后看到addLenient中是先加name再加value,所以到這里你就能知道namesAndValues的數組是怎么存值的。
其實這些都不是很重要,簡單了解一下就行。
(2)RequestBody請求體
它是一個抽象類,然后實體類,其實我這里主要是為了看這個,驗證我上一篇講http的請求體
可以看到請求的body是分普通情況和文件的情況。
這些都不重要。
3.Call
這個就是其中一個重要的東西了。
可以看到是用OkHttpClient的newCall方法創建Call,也驗證了OkHttpClient就是Call的工廠。
Call是一次請求的抽象。既然是抽象,那就需要一個實現,那就是這里的RealCall,記住它代表一次請求,它代表一次請求,它代表一次請求,這一聽是沒什么,但是它這樣的一個思想我覺得是很好的,你想想一次請求是什么,是一個行為,一個過程,它這里把行為進行抽象,從而方便于進行管理與操作。
RealCall有4個屬性
(1)它這里引用了OkHttpClient,我覺得是因為OkHttpClient里面保存了和整個過程相關的參數。但是這樣相互引用的話耦合度會不會有點高。
(2)RetryAndFollowUpInterceptor是一個攔截器,后面會說。
(3)executed用來記錄請求是否執行,一個請求只能執行一次
(4)originalRequest就是傳進來的Request。
這個請求類,它的行為主要是一些請求的操作,比如開始同步請求,開始異步請求這些。所以如果你想對“請求”做什么操作,可以先來這個類來找找有沒有對應的方法,比如取消請求,就在這里。
看看執行同步請求的方法。
第一個判斷請求是否執行夠,因為一個請求只允許執行一次。主要的操作是這3行
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
client.dispatcher().finished(this);
這里有涉及到client.dispatcher(),這是OkHttpClient的一個參數,看看是個啥子。Dispatcher,它又是一個okhttp里面比較重要的對象,表示檢測者,負責監視整個請求的過程,它內部有很多的參數。
最大請求數maxRequests,最大主機請求數maxRequestsPerHost(好吧,其實我也不懂這是啥),有個Runnable,然后ExecutorService線程池,還有三個隊列readyAsyncCalls、runningAsyncCalls和runningSyncCalls。
我講講這三個隊列的意思,runningSyncCalls是存儲同步請求,也就是Call(其實這里我有點蒙,你想想,同步請求如果有多個的話肯定是個排隊請求的過程,而每個請求結束后都會移除隊列,那這樣的話這個隊列不是一直都只有當前進行的請求嗎,這樣做有什么意義)。runningAsyncCalls是存儲異步請求的隊列,這個很有必要,因為異步是同時進行的,所以可以用個隊列來存儲,readyAsyncCalls表示準備進行異步請求的隊列,因為有最大請的限制。
扯多了,看看同步請求時的executed方法和finished方法。
executed就是把請求添加到同步請求隊列。
finish就是把請求從隊列中移除。這個run()方法我不知道,我沒找到在哪。
看完同步再來看看異步
異步的具體操作是寫在Dispatcher里面
判斷當前異步的隊列數量做比較,超過了最大值就把Call添加到準備隊列,否則添加到執行隊列并執行線程,而AsyncCall繼承了Runnable,所以executorService().execute(call)會執行call的run方法。這個run在內部又調用了execute方法,來看看這個方法。
@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);
}
}
}
這個也不難看懂吧,主要兩行代碼
Response response = getResponseWithInterceptorChain();
client.dispatcher().finished(this);
finished和上面的一樣講過了,不同的是在里面多調用了個promoteCalls方法,其實就是對runningAsyncCalls、readyAsyncCalls兩個隊列做操作,也不難看懂,影響不大。
總結一下,不管是同步還是異步,做的操作都是 添加進隊列——>調用getResponseWithInterceptorChain()方法——>從隊列中移除。
到這里是不是就很容易看出getResponseWithInterceptorChain()就是請求網絡整個過程的核心操作。
4.getResponseWithInterceptorChain()方法
我覺得這個就是整個okhttp里面最核心的操作,這個方法就是按順序調用個個攔截器對請求進行處理。說得好聽一點,這個叫責任鏈模式。
什么是責任鏈模式,不懂的我覺得有必要百度一波。簡單點說就是一個請求就來,你對這個請求做處理或者不做處理,然后傳給下一個人,一個一個的傳。不了解的話不太容易看懂我下面的一些話。
getResponseWithInterceptorChain方法是RealCall類里面的,也可以說,這個請求執行了責任鏈的一系列操作。
interceptors表示存儲攔截器的列表,進行添加一些列的攔截器操作之后,創建攔截器鏈對象,然后執行proceed方法就能返回請求的響應Response。
那么重心又到了RealInterceptorChain這個鏈對象的身上。
先要注意一下它的這些參數。
(1)interceptors是攔截器列表
(2)streamAllocation 一開始傳空進來
(3)httpStream 一開始傳空進來
(4)connection 一開始傳空
(5)index表示當前鏈的一個下標。我上邊也說了,責任鏈執行操作后丟給下一個執行,這里就是每次丟給下一個攔截器的時候index加一。所以初始肯定是0
(6)request就是請求,包含什么參數之前也有寫。
簡單了解一下參數后看看proceed,看看具體的請求數據的流程是咋樣的。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection 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.
if (this.httpStream != null && !sameConnection(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().
if (this.httpStream != 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, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != 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;
}
先這樣,所有關于報錯的判斷我們先不管,抽出核心的代碼。
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
它先創建一個新的責任鏈對象,然后下標加1,然后讓第一個攔截器Interceptor 執行intercept操作,其實這個操作里面會調用新的責任鏈的proceed方法,這樣就使得整個責任鏈模式給運作起來。其實我是感覺這個有點繞的,反正就是創建一個新的鏈對象,把鏈對象傳給攔截器,然后攔截器里面又調用鏈對象的proceed方法,這個方法又重復這些操作直到最后一個攔截器。
稍微理清楚后來看看這個鏈上攔截器的一個順序
其實就是列表中的順序
先添加用戶自定義的攔截器,這個我們后面再說吧。然后按這樣的順序添加
(1)RetryAndFollowUpInterceptor重試和重定向攔截器
(2)BridgeInterceptor 橋梁攔截器(我喜歡叫它配置請求攔截器)
(3)CacheInterceptor 緩存攔截器
(4)ConnectInterceptor 連接攔截器
(5)interceptors.addAll(client.networkInterceptors()); 是添加用戶定義的網絡請求攔截器(所以可以看出用戶可以定義兩種攔截器)
(7)CallServerInterceptor 內部的網絡攔截器
因為我也并不是能完全的看懂這些攔截器內部的所有代碼,所以我就不講攔截器內詳細做的操作了,免得誤人子弟,我當時是看了這篇文章
https://blog.csdn.net/qq_19431333/article/details/53207220
其實還是看不懂詳細的代碼。