前言:OkHttp框架是Android的網絡請求框架,無數的項目都在使用着這個框架,重要性不言而喻;html
源碼基於okhttp3 java版本:3.14.9java
public void request() {
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get() //默認就是GET請求,能夠不寫
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "onResponse: " + response.body().string());
}
});
}
複製代碼
咱們將使用到的這些類拆分出來,逐個講解;android
OkHttpClient是什麼?git
顧名思義,咱們能夠理解爲是OkHttp的客戶端;github
咱們來看一下這個類裏面有啥東西?編程
首先,從源碼裏面能夠看到聲明瞭一大堆成員變量,咋一看,還有點看不懂,涉及的東西太多了,可是不要緊,咱們的目的是爲了瞭解這個類的職責;設計模式
下面咱們先來看兩張圖:api
圖一:緩存
圖二:服務器
怎樣,看了上面兩張圖,是否是有種熟悉的感受,是否是很像咱們寫Gson解析時的bean類;
沒錯,這個OkHttpClient裏面就是這樣的代碼,相似於bean類,主要作一些參數的賦值與獲取;
那麼到這裏咱們就明朗了,OkHttpClient裏面並無很複雜的邏輯,整個類的邏輯反而很清晰,就是實現參數的管理;
至於裏面的一些參數是幹嗎的,這裏咱們先無論,只要明白整個類的職責是啥就好了;
Request,這個單詞的意思是請求,那麼這個類裏面是作的這個操做嗎?
咱們來看一下源碼,一窺究竟;
先來看一下這個成員變量,不多,就幾個,可是從命名能夠看出,這裏面定義了請求的URL,請求的method,還有請求頭headers,請求體body等參數;
再往下看;
從這裏能夠看出,源碼裏面也是定義了一些參數的賦值與獲取,那麼也是和bean類的邏輯差很少;
這裏面其實沒沒作啥特殊操做,主要是經過建造者模式,根據參數來建立Request類;
看一下Request的Builder的邏輯:
這裏只用做示例,沒必要關心代碼細節,咱們只須要了解這個類的職責,就是根據請求的參數構建實體類;
Response,顧名思義,爲請求的返回體;
那麼這個類裏面究竟實現了什麼邏輯呢?
先來看一下成員變量:
從圖片能夠看出,Response和Request有點相似,封裝了一些參數返回,好比code,message,headers,body等等;
在來看一下這張圖片:
也是定義了參數的賦值與獲取,這裏就再也不贅述;
Response類裏面主要定義了服務器返回的相關信息,這裏面也沒有其餘的複雜邏輯,把它當成一個bean類來理解便可;
ReallCall,顧名思義,咱們能夠先理解爲真正的請求,那麼咱們接下來來看源碼求證一下,看看是否是真的是這樣;
首先,先來看一下參數:
從圖片能夠看出,只有幾個參數,都是在構造方法初始化的;
這裏面初始化了Transmitter,這個咱們後面再講;
那麼咱們再來看一下它這裏面有哪些方法:
這個幾個方法有沒有熟悉的感受,這裏實際上是經過橋接設計模式,實際調用的是OkHttpClient和Transmitter的函數;
這幾個方法是網絡請求的最核心的方法,execute()方法其實是同步請求,在當前線程就觸發了網絡請求,經過getResponseWithInterceptorChain()來執行網絡請求從而獲取到Response返回結果;
而enqueue(Callback responseCallback)方法,是在子線程中執行的,經過建立一個AsyncCall,傳遞給client.dispatcher(),這裏面主要是線程的執行邏輯;
這個AsyncCall是實現了Runnable接口,具體執行是在AsyncCall的execute()方法裏面;
咱們來看一下AsyncCall的execute()方法具體實現邏輯;
這裏面主要有兩個方法,一個是executeOn(),經過入參傳遞一個線程池,來執行當前線程,另外一個方法是execute(),爲異步的具體實現,讓咱們來看看這個方法作了啥?
和上面同步調用的邏輯同樣,也是經過getResponseWithInterceptorChain()來執行網絡請求從而獲取到Response返回結果;
還有這個方法getResponseWithInterceptorChain(),經過攔截器+責任鏈設計模式來實現網絡請求,這個咱們下面再介紹,這個只要瞭解是經過這個來進行網絡請求的便可;
小結: ReallCall的職責是進行真正的網絡請求,裏面封裝了兩個方法,一個是同步請求execute(),一個是異步請求enqueue(),還有最後也是最重要的一個方法getResponseWithInterceptorChain(),經過這個方法來執行網絡請求;
咱們在看ReallCall源碼的時候,頻頻見到這個方法,client.dispatcher().調用,這個方法的真面目就是Dispatcher,這個Dispatcher是在建立OkHttpClient的時候進行初始化的;
接下來咱們來看看這個類的職責是作什麼的;
老規矩,先來看當作員變量:
參數很少,咱們來一個個介紹:
下面來說一講這個類幾個比較重要的方法;
(1)線程池的建立:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
複製代碼
核心線程爲0,每隔60秒會清空空閒的線程,而最大線程無限制,可是已經經過成員變量來進行控制了,沒啥影響;
(2)同步請求的執行:
同步請求的執行是這個方法,具體調用是在ReallCall的excute()方法裏面,在Dispatcher先經過調用executed(),後面再調用finish()方法來移除同步請求;
來看一下這個finish()方法;
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
// 移除隊列的請求
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
// 執行請求
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
// 觸發空閒線程執行
idleCallback.run();
}
}
複製代碼
這個方法的邏輯很簡單,先移除隊列裏的call,而後再調用promoteAndExecute()執行已經準備好執行的請求,這個邏輯咱們下面再講;
(3)異步請求的執行:
void enqueue(AsyncCall call) {
synchronized (this) {
// 添加請求到異步隊列;
readyAsyncCalls.add(call);
if (!call.get().forWebSocket) {
// 判斷當前請求是否已經存在
AsyncCall existingCall = findExistingCallWithHost(call.host());
// 若是當前請求已經存在,則複用以前的線程計數,不進行遞增;
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
// 執行請求
promoteAndExecute();
}
複製代碼
這個方法裏面,先將請求添加到異步隊列readyAsyncCalls裏面,而後再調用promoteAndExecute()方法來觸發請求,下面咱們來看看promoteAndExecute()的邏輯;
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
// 一、遍歷準備要執行的請求隊列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 二、判斷當前正在執行的請求個數大於最大請求個數時,則取消請求
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
// 三、判斷當前主機的鏈接數超過5個時,則跳過當前請求;
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
// 添加請求到正在執行的隊列中
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
// 執行請求;
asyncCall.executeOn(executorService());
}
return isRunning;
}
複製代碼
這個promoteAndExecute()方法的主要邏輯是執行readyAsyncCalls隊列裏的請求,而maxRequests和maxRequestsPerHost也是在這裏處理的;
除此以後,這個類裏面還有一些查詢的方法,好比queuedCalls(),runningCalls()等,不過這些不是重點,瞭解便可;
攔截器,能夠說是OkHttp最重要的部分了,這一部分經過一個很巧妙的設計,將複雜的網絡請求邏輯分散到每一個攔截器中,這種設計模式就叫作責任鏈;
責任鏈模式的優勢:下降耦合度,簡化對象,加強對象的靈活性;
缺點:性能會受到必定的影響;
那麼對於網絡請求,最簡單的實現就是從0到1,也就是我直接請求,不考慮異常因素,保證每次請求都能成功,那麼這個實現就很簡單,只須要調用請求網絡的api進行請求數據便可;
可是現實每每是殘酷的,網絡的環境極其複雜,而每一次的請求也不必定能返回,因此咱們須要使用各類策略來保證網絡請求能夠正常完成,好比重試,緩存等操做來保證網絡請求的正常使用;
而OkHttp的網絡請求經過責任鏈設計了幾個攔截器,巧妙的經過責任鏈模式來處理複雜的網絡請求,避免了類的臃腫而且提供了很好的擴展性,缺點就是當責任鏈上的對象過多時,可能會出現性能的問題;
那麼接下來咱們來看看OkHttp的攔截器的實現吧;
OkHttp的攔截器有:
下面咱們來一個個具體分析;
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 準備鏈接請求
transmitter.prepareToConnect(request);
...
Response response;
boolean success = false;
// 執行其餘攔截器的功能,獲取Response;
response = realChain.proceed(request, transmitter, null);
// 根據Response的返回碼來判斷要執行重試仍是重定向;
Request followUp = followUpRequest(response, route);
...
if (followUp == null) {
// 若是followUpRequest返回的Request爲空,那邊就表示不須要執行重試或者重定向,直接返回數據;
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
// 若是followUp爲null,請求體不爲空,而且只須要請求一次時,那麼就返回response;
return response;
}
// 判斷重試或者重定向的次數是否超過最大的次數,是的話則拋出異常;
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 將須要重試或者重定向的請求賦值給新的請求;
request = followUp;
}
}
複製代碼
followUpRequest方法的邏輯咱們大概瞄一眼,就是根據返回碼來作一下操做;
橋接攔截器,這裏主要作網絡請求的封裝,用於簡化應用層的邏輯,好比網絡請求須要傳"Transfer-Encoding","Accept-Encoding","User-Agent","Cookie"這些參數,可是應用層不須要關心這些,那麼就由這個攔截器來作這些封裝;
當請求完成以後,也會對Response的header作一下封裝處理,返回給應用層,這樣應用層就不須要關心這個header的細節,簡化操做;
public Response intercept(Chain chain) throws IOException {
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
// 處理封裝"Content-Length","Transfer-Encoding","Host","Connection","Accept-Encoding","Cookie","User-Agent"等請求頭;
// 執行後續的攔截器的邏輯
Response networkResponse = chain.proceed(requestBuilder.build());
// 獲取返回體的Builder
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
...
//處理返回的Response的"Content-Encoding"、"Content-Length"、"Content-Type"等返回頭;
...
return responseBuilder.build();
}
複製代碼
CacheInterceptor是緩存處理的攔截器,咱們先來看一下這個攔截器的邏輯;
public Response intercept(Chain chain) throws IOException {
// 先獲取候選緩存,前提是有配置緩存,也就是cache不爲空;
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 獲取緩存策略;
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
// 緩存策略的請求類
Request networkRequest = strategy.networkRequest;
// 獲取的本地的緩存;
Response cacheResponse = strategy.cacheResponse;
// 若是被禁止使用網絡數據且緩存數據爲空,那麼返回一個504的Response,而且body爲空;
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
...
.build();
}
// 若是不須要使用網絡數據,那麼就直接返回緩存的數據;
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
// 執行後續的攔截器邏輯;
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
...
}
if (cacheResponse != null) {
// 若是緩存數據不爲空而且code爲304,表示數據沒有變化,繼續使用緩存數據;
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder().xx.build();
...
// 更新緩存數據
cache.update(cacheResponse, response);
return response;
}
}
// 獲取網絡返回的response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
// 將網絡數據保存到緩存中;
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
// 緩存失效,那麼就移除緩存
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
複製代碼
在這個獲取緩存策略這一步,會生成一個CacheStrategy對象,用於管理緩存策略,那麼在將緩存策略以前,咱們先來了解幾個概念;
強緩存:在請求數據的時候,查看請求頭expires和cache-control是否命中緩存,若是是的話,那麼久就會從緩存中獲取數據,不會走網絡請求;
協商緩存:而協商緩存是在沒有命中強緩存的狀況下才會走的邏輯,必會走一次網絡請求,經過last-modified和etag返回頭判斷是否命中緩存,若是沒有命中,那麼就走網絡從新獲取到數據,協商緩存須要服務器支持才能實現;
那麼接下來咱們來看一下OkHttp是怎麼實現緩存策略邏輯的;
若是對於緩存邏輯不是很清楚的話,能夠看一下這篇文章:Android 你不得不學的HTTP相關知識
從上面那個方法,咱們來看看new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get()的邏輯;
private CacheStrategy getCandidate() {
// 若是不使用緩存,那麼就返回一個空的Response的CacheStrategy;
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}
// 強緩存
long ageMillis = cacheResponseAge();
long freshMillis = computeFreshnessLifetime();
// 判斷強緩存是否有效,是的話就返回緩存數據;
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
// 協商緩存
String conditionName;
String conditionValue;
if (etag != null) {
// etag協商緩存
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
// Last-Modified協商緩存
conditionName = "If-Modified-Since";
// 最後修改時間
conditionValue = lastModifiedString;
} else if (servedDate != null) {
// Last-Modified協商緩存
conditionName = "If-Modified-Since";
// 服務器最後修改時間
conditionValue = servedDateString;
} else {
// 沒有協商緩存,返回一個空的Response的CacheStrategy;
return new CacheStrategy(request, null); // No condition! Make a regular request.
}
// 設置header
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
return new CacheStrategy(conditionalRequest, cacheResponse);
}
複製代碼
這個攔截器比較重要,網絡的最底層實現都是經過這個類,這裏面封裝了socket鏈接和TLS握手等邏輯,接下來咱們來看看具體是怎麼實現的;
public Response intercept(Chain chain) throws IOException {
...
// 這簡單的一行代碼,卻實現了無比複雜的網絡請求,Exchange用於下一個攔截器CallServerInterceptor進行網絡請求使用;
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
// 執行後續的攔截器邏輯
return realChain.proceed(request, transmitter, exchange);
}
複製代碼
這個類主要實現瞭如下幾個步驟的邏輯:
這個攔截器主要是實現網絡鏈接的邏輯,而網絡請求的邏輯是放在CallServerInterceptor這個攔截器中實現的;
那麼上面咱們講完這個攔截器的基本邏輯,下面咱們來看看更深層次的知識;
對於鏈接來講,最簡單的實現就是每次須要的時候都進行建立並鏈接,不須要考慮網絡環境,以及資源等等因素,可是現實狀況咱們不可能這樣作,由於若是要追求極致的體驗,咱們就必須得作優化;
而這裏的優化就是鏈接複用;
下面咱們來看看源碼是怎麼實現複用邏輯的,上面咱們瞭解到建立RealConnection是經過ExchangeFinder的findConnection方法,那麼咱們來看看這個方法裏的具體邏輯;
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
...
synchronized (connectionPool) {
...
if (result == null) {
// 嘗試從緩存池中獲取可用的鏈接
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
foundPooledConnection = true;
result = transmitter.connection;
} else if (nextRouteToTry != null) {
// 若是獲取不到,那麼久從Route裏面獲取
selectedRoute = nextRouteToTry;
nextRouteToTry = null;
} else if (retryCurrentRoute()) {
// 若是當前的路由是重試的路由,那麼就從路由裏面獲取
selectedRoute = transmitter.connection.route();
}
}
}
if (result != null) {
// 若是獲取到鏈接,那麼就直接返回結果
return result;
}
// 若是前面沒有獲取到鏈接,那麼這裏就經過RouteSelector先獲取到Route,而後再獲取Connection;
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
// 經過RouteSelector獲取Route
routeSelection = routeSelector.next();
}
//
List<Route> routes = null;
synchronized (connectionPool) {
if (newRouteSelection) {
// 經過RouteSelector拿到Route集合(IP地址),再次嘗試從緩存池中獲取鏈接,看看是否有能夠複用的鏈接;
routes = routeSelection.getAll();
if (connectionPool.transmitterAcquirePooledConnection(
address, transmitter, routes, false)) {
...
}
}
if (!foundPooledConnection) {
// 若是上面沒有獲取到,那麼就建立一個新的鏈接
result = new RealConnection(connectionPool, selectedRoute);
connectingConnection = result;
}
}
// 開始TCP握手和TSL握手,這是一個阻塞的過程;
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
connectionPool.routeDatabase.connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
connectingConnection = null;
// 將鏈接成功的RealConnection放到緩存池中,用於後續複用
connectionPool.put(result);
transmitter.acquireConnectionNoEvents(result);
}
return result;
}
複製代碼
好了,上面的鏈接複用已經講完了,下面咱們來看看鏈接的具體邏輯,也就是connect的方法的具體邏輯;
public void connect(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, Call call, EventListener eventListener) {
if (route.requiresTunnel()) {
// 若是是HTTP的代理隧道,那麼就會走代理隧道的加載邏輯;
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
} else {
// 走正常的鏈接邏輯,無代理
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
// 創建協議,這裏會觸發TLS的握手
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
複製代碼
這個方法主要分三步:
那麼接下來咱們來看看TLS握手的具體實現,調用方法是RealConnection的connectTls方法;
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
// 建立SSLSocket,用於鏈接;
sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
// 配置SSLSocket的密碼,TLS版本和擴展信息
ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
// 開始TLS握手
sslSocket.startHandshake();
// 獲取sslSocketSession,用於創建會話
SSLSession sslSocketSession = sslSocket.getSession();
Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
// 驗證目標主機的證書
if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
...
}
// 檢查服務器提供的證書是否在固定證書裏面
address.certificatePinner().check(address.url().host(),
unverifiedHandshake.peerCertificates());
// 鏈接成功,保存握手信息和ALPN協議
String maybeProtocol = connectionSpec.supportsTlsExtensions()
? Platform.get().getSelectedProtocol(sslSocket)
: null;
...
}
複製代碼
這個connectTls方法主要作了幾個操做:
第四步若是看不懂的,能夠參考一下這個連接,寫的很詳細;
若是對於TLS握手還不是很清楚的,能夠看一下我以前寫的這篇文章:
在上一個攔截器ConnectInterceptor裏,咱們已經和服務器創建起鏈接了;
那麼接下來就是想服務器發送header和body以及接收服務器返回的數據了,這些邏輯都在這個攔截器中;
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();
// 將請求頭寫入到socket中,底層經過ExchangeCodec協議類(對應Http1ExchangeCodec和Http2ExchangeCodec),最終是經過Okio來實現的,具體實如今RealBufferedSink這個類裏面
exchange.writeRequestHeaders(request);
// 若是有body的話,經過Okio將body寫入到socket中,用於發送給服務器;
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
// 底層經過ExchangeCodec協議類(對應Http1ExchangeCodec和Http2ExchangeCodec)來讀取返回頭header的數據;
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
...
// 建立返回體Response;
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
...
// 底層經過ExchangeCodec協議類(對應Http1ExchangeCodec和Http2ExchangeCodec)來讀取返回體body的數據;
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
return response;
}
複製代碼
這個類的邏輯就是socket相關的操做了,在這裏經過Okio將數據寫入到socket和從socket中讀取服務器返回的數據;
OkHttp的源碼到這裏就大體分析完了,固然還有一些細節沒有涉及到,好比DNS,cookie等等,感興趣的能夠本身跟蹤源碼去分析,這裏就再也不深刻探究了;