OKHttp是Square公司辨析的一個網絡請求框架,也是目前市面上使用最多的網絡框架之一。OKHttp是基於HTTP協議封裝的一套請求客戶端,在請求底層支持鏈接同一個地址的連接共享同一個Socket。java
OkHttp做爲當前Android端最火熱的網絡請求框架之一,有不少的優勢:web
這篇文章主要針對OKHttp的工做原理進行分析,着重介紹OKHttp實現的原理以及工做流程。算法
如下是基於OKHttp 3.9.x分析緩存
首先,咱們先來看下OKHttp的使用。cookie
OkHttpClient client = new OkHttpClient();//建立OkHttpClient對象
Request request = new Request.Builder()
.url(url)//請求連接
.build();//建立Request對象
Response response = client.newCall(request).execute();//獲取Response對象
複製代碼
以上代碼是OKHttp的GET請求的同步請求用法。能夠看到,第一步是建立OKHttpClient對象,而後建立Request,最後發起請求並獲取請求結果Response。咱們針對上面的請求流程開始分析OKHttp的工做原理。網絡
從代碼中能夠看出,在使用OkHttp時須要先建立OkHttpClient對象。框架
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
//......
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
複製代碼
上面的代碼就是OkHttpClient的構造方法。能夠看到OkHttpClient有兩個構造方法,在構造方法中咱們能夠看到會初始化一個Builder對象(OKHttp使用了建造者模式),根據構造方法的代碼,很容易發如今構造方法中主要設置了一些OKHttp的屬相。好比:超時設置、攔截器、HTTPS相關等。異步
接下來開始建立Request對象,Request描述了OkHttp將要發送的請求。好比:URL、HTTP header、請求類型(GET請求或者POST請求)等。socket
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;
}
複製代碼
能夠看到Request也是經過建造者模式建立的,在這裏配置了url、請求頭等信息。async
在上面OKHttpClient和Request建立好以後,就開始發起HTTP請求了。OkHttp中請求方式分爲同步請求(client.newCall(request).execute() )和異步請求(client.newCall(request).enqueue())兩種,其中同步請求和一部請求的區別就是同步請求會阻塞當前線程,一部請求會放到線程池中執行。
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
複製代碼
能夠看到經過newCall()方法建立了RealCall實例,而後經過RealCall發起請求。接下來咱們同步OkHttp的異步請求分析。異步請求調用了RealCall的enqueue()方法。
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製代碼
在這裏,OkHttp經過調度器Dispatcher執行請求。
/**Dispatcher**/
synchronized void enqueue(AsyncCall call) {
//這裏判斷隊列是否已滿,隊列不滿怎將請求放到線程池中執行,不然加入到隊列中
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
複製代碼
能夠看到enqueue()方法是一個同步方法,在這裏首先判斷了請求隊列是否已滿,若是不滿,則開始在線程池中執行請求AsyncCall。AsyncCall繼承了NamedRunnable抽象類,而NamedRunnable繼承了Runnable接口,在run方法中調用了execute()方法。
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);
}
}
//......
finally {
client.dispatcher().finished(this);
}
}
複製代碼
在這裏開始了OkHttp核心的請求部分。在OkHttp中使用了責任鏈模式處理這一部分的請求。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)); //鏈接攔截器,建立HTTP鏈接
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket)); //網絡請求攔截器,開始網絡請求
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
複製代碼
在上面的代碼中OkHttp經過各類攔截器處理請求。這裏簡單介紹下OkHttp的攔截器:
攔截器是OkHttp發起請求的核心部分,接下來咱們針對各類攔截器進行分析。上面的代碼中,經過RealInterceptorChain的proceed()方法開始執行攔截器。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
calls++;
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); //執行攔截器
//......
return response;
}
複製代碼
這裏咱們直接分析RetryAndFollowUpInterceptor的intercept()方法。
public Response intercept(Chain chain) throws IOException {
//......
int followUpCount = 0;
Response priorResponse = null;
//經過一個循環來從新嘗試請求
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
//1.調用下一個攔截器
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
//......
} catch (IOException e) {
//......
}
//......
//2.檢測response是否合法
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
//3.返回response,請求完成
return response;
}
//最多嘗試20次
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//4.從新設置請求
request = followUp;
priorResponse = response;
}
}
複製代碼
在RetryAndFollowUpInterceptor中咱們能夠看到請求的重試是由一個無限循環保持的,同時在代碼裏還限制了請求的次數,最多嘗試20次。RetryAndFollowUpInterceptor的具體邏輯是:
咱們看看BridgeInterceptor作了哪些事。
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
複製代碼
從代碼裏能夠看到,在BridgeInterceptor中出了HTTP的請求頭,設置了請求頭的各類參數,好比:Content-Type、Connection、User-Agent、GZIP等。
緩存攔截器主要是處理HTTP請求緩存的,經過緩存攔截器能夠有效的使用緩存減小網絡請求。
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null? cache.get(chain.request()): null;//1.取緩存
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //2.驗證緩存
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse; //獲取緩存
if (cache != null) {
cache.trackResponse(strategy);
}
// If we're forbidden from using the network and the cache is insufficient, fail.
//這裏表示禁止使用緩存
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
//3.直接返回緩存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//4.沒有緩存,執行下一個攔截器
networkResponse = chain.proceed(networkRequest);
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
//5.更新緩存
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//......
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
//6.保存緩存
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
}
return response;
}
複製代碼
在上面的代碼中能夠看到,OkHttp首先會取出緩存,而後通過驗證處理判斷緩存是否可用。流程以下:
緩存攔截器主要的工做就是處理緩存,知道了大體流程後,咱們接下來分析一下OkHttp是如何管理緩存的。首先咱們分析緩存如何獲取,在代碼中能夠看到經過cache.get()獲得,咱們直接跟代碼看。
final InternalCache internalCache = new InternalCache() {
@Override public Response get(Request request) throws IOException {
return Cache.this.get(request);
}
@Override public CacheRequest put(Response response) throws IOException {
return Cache.this.put(response);
}
@Override public void remove(Request request) throws IOException {
Cache.this.remove(request);
}
@Override public void update(Response cached, Response network) {
Cache.this.update(cached, network);
}
@Override public void trackConditionalCacheHit() {
Cache.this.trackConditionalCacheHit();
}
@Override public void trackResponse(CacheStrategy cacheStrategy) {
Cache.this.trackResponse(cacheStrategy);
}
};
複製代碼
能夠看到,緩存是經過InternalCache管理的,而InternalCache是Cache的內部了類,InternalCache又調用了Cache的方法。咱們這裏只分析一個get()方法。
@Nullable Response get(Request request) {
String key = key(request.url());
DiskLruCache.Snapshot snapshot;
Entry entry;
try {
snapshot = cache.get(key);
if (snapshot == null) {
return null;
}
} catch (IOException e) {
return null;
}
try {
entry = new Entry(snapshot.getSource(ENTRY_METADATA));
} catch (IOException e) {
Util.closeQuietly(snapshot);
return null;
}
Response response = entry.response(snapshot);
//......
return response;
}
複製代碼
能夠看到,緩存是經過DiskLruCache管理,那麼不難看出OkHttp的緩存使用了LRU算法管理緩存。接下來,咱們分析下OkHttp如何驗證緩存。
在上面的代碼中,緩存最終來自於CacheStrategy。咱們直接分析下那裏的代碼。
private CacheStrategy getCandidate() {
// No cached response.
if (cacheResponse == null) {
//1.沒有緩存,直接返回沒有緩存
return new CacheStrategy(request, null);
}
if (request.isHttps() && cacheResponse.handshake() == null) {
//2.沒有進行TLS握手,直接返回沒有緩存
return new CacheStrategy(request, null);
}
if (!isCacheable(cacheResponse, request)) {
//3.判斷是不是可用緩存。這裏是根據cache-control的屬性配置來判斷的
return new CacheStrategy(request, null);
}
CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
//4.cache-control:no-cache不接受緩存的資源;根據請求頭的"If-Modified-Since"或者"If-None-Match"判斷,這兩個屬性須要到服務端驗證後才能判斷是否使用緩存,因此這裏先不使用緩存
return new CacheStrategy(request, null);
}
CacheControl responseCaching = cacheResponse.cacheControl();
if (responseCaching.immutable()) {
//5.cache-control:imutable 表示響應正文不會隨時間而改變,這裏直接使用緩存
return new CacheStrategy(null, cacheResponse);
}
long ageMillis = cacheResponseAge();
long freshMillis = computeFreshnessLifetime();
if (requestCaching.maxAgeSeconds() != -1) {
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}
long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) {
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}
long maxStaleMillis = 0;
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
}
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\"");
}
//6.這裏根據時間計算緩存是否過時,若是不過時就使用緩存
return new CacheStrategy(null, builder.build());
}
String conditionName;
String conditionValue;
if (etag != null) {
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
conditionName = "If-Modified-Since";
conditionValue = lastModifiedString;
} else if (servedDate != null) {
conditionName = "If-Modified-Since";
conditionValue = servedDateString;
} else {
//7.沒有緩存驗證條件,須要請求服務端
return new CacheStrategy(request, null); // No condition! Make a regular request.
}
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
//8.這裏將上面的驗證條件加入請求頭,繼續向服務端發起請求
return new CacheStrategy(conditionalRequest, cacheResponse);
}
複製代碼
從上面的代碼能夠看到,OkHttp通過不少判斷才能肯定是否使用緩存。判斷過程能夠總結爲:
在上面的驗證過程當中主要經過Cache-Control中的屬性判斷緩存是否可用,若是可用則直接返回緩存,不然像服務端繼續發送請求判斷緩存是否過時。
ConnectInterceptor的做用就是創建一個與服務端的鏈接。
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
複製代碼
在上面的代碼中,能夠看到鏈接來自於StreamAllocation的newStream()方法。
public HttpCodec newStream( OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
複製代碼
能夠看到在newStream()方法中會繼續尋找鏈接。咱們繼續分析代碼能夠看到,OkHttp的鏈接是維護在一個鏈接池中的。
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
RealConnection result = null;
Route selectedRoute = null;
Connection releasedConnection;
Socket toClose;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new streams.
releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
// We had an already-allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
if (result == null) {
// Attempt to get a connection from the pool.
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
result = connection;
} else {
selectedRoute = route;
}
}
}
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
List<Route> routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(
connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
複製代碼
以上是OkHttp獲取鏈接的主要邏輯,方法比較複雜,咱們這裏總結一下獲取鏈接的流程,具體的細節能夠自行查看。
CallServerInterceptor是最後一個攔截器,理所固然這個攔截器負責向服務端發送數據。
public Response intercept(Chain chain) throws IOException {
//......
//寫入請求頭數據
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
//......
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
//這裏寫入請求體
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
streamAllocation.noNewStreams();
}
}
//完成請求
httpCodec.finishRequest();
if (responseBuilder == null) {
//這裏請求返回,讀取返回請求頭
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
int code = response.code();
if (forWebSocket && code == 101) {
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
//讀取返回內容
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
//......
return response;
}
複製代碼
在上面的代碼上能夠看到主要是由HttpCodec執行的數據寫入以及讀取。HttpCodec是一個接口,它實現有兩個類,分別是Http1Codec(處理HTTP1.1請求)和Http2Codec(處理HTTP2請求)。在HttpCodec的實現中主要經過okio與服務端通訊。在上一節的ConnectInterceptor咱們知道,OkHttp與服務端創建了一個TCP鏈接,因此客戶端的與服務端的通訊是直接經過TCP協議層的,當數據返回時,OkHttp會將數據構造HTTP形式的數據。
OkHttp的工做原理就分析到這裏了。在上面的文章中,首先分析了OkHttp在發起請求的準備階段工做,構造OkHttpClient以及Request,而後經過調度器Dispatcher處理請求任務(請求又分爲同步請求和異步請求)。最後經過攔截器處理請求。攔截器做爲OkHttp中處理請求的核心部分,咱們再文章中對各類攔截器都進行了分型,固然其中還有不少細節沒有講到,感興趣的同窗能夠更加深刻的去了解。