OkHttp 源碼解析

前言

OkHttp可以說是最主流的網(wǎng)絡(luò)請求框架了,很多項目是直接使用Retrofit 2.0提供的接口進行網(wǎng)絡(luò)請求,Retrofit 是一個 RESTful 的 HTTP 網(wǎng)絡(luò)請求框架的封裝。想了解Retrofit請移步Retrofit 2.0 源碼分析,retrofit是負責(zé)接口封裝,okhttp才是真正的網(wǎng)絡(luò)請求,今天我們就一起探究整個okhttp請求的過程,本章節(jié)并不會著重講怎么使用,主要閱讀源碼了解內(nèi)部的部分機制以及一些核心類的作用。

初始化OkHttpClient

OkHttpClient mOkHttpClient = new OkHttpClient();  

OkHttpClient實例是通過建造者模式通過Builder類進行創(chuàng)建的

 public OkHttpClient() {  
 this(new Builder());  
}  

我們看一下builder初始化了哪些參數(shù)

public Builder() {  
 dispatcher = new Dispatcher();  
 protocols = DEFAULT_PROTOCOLS;  
 connectionSpecs = DEFAULT_CONNECTION_SPECS;  
 proxySelector = ProxySelector.getDefault();  
 cookieJar = CookieJar.NO_COOKIES;  
 socketFactory = SocketFactory.getDefault();  
 hostnameVerifier = OkHostnameVerifier.INSTANCE;  
 certificatePinner = CertificatePinner.DEFAULT;  
 proxyAuthenticator = Authenticator.NONE;  
 authenticator = Authenticator.NONE;  
 connectionPool = new ConnectionPool();  
 dns = Dns.SYSTEM;  
 followSslRedirects = true;  
 followRedirects = true;  
 retryOnConnectionFailure = true;  
 connectTimeout = 10_000;  
 readTimeout = 10_000;  
 writeTimeout = 10_000;  
}  

構(gòu)造一個Request,也是通過建造者模式創(chuàng)建的

Request request = new Request.Builder()    
.url(url)    
.build(); 

Request類是HTTP請求,它攜帶了請求地址、請求方法、請求頭部、請求體以及其他信息。它也是通過Builder模式創(chuàng)建的。

private 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;  
}  

開始請求

Response  response = mOkHttpClient.newCall(request).execute();

看一下 OkHttpClient.newCall(request),做了什么,RealCall類實現(xiàn)了Call接口,下面展示RealCall()方法的代碼

protected RealCall(OkHttpClient client, Request originalRequest) {  
this.client = client;  
this.originalRequest = originalRequest;  
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);  
}  

接著是RealCall的execute()方法。execute()方法是同步方法,即一直等待http請求, 直到返回了響應(yīng). 在這之間會阻塞進程, 所以通過同步方法不能在Android的主線程中執(zhí)行, 否則會報錯。

OKHttp提供了execute(同步方法)和enqueue(異步方法),下面我們先看看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);
}
}

execute()方法,首先判斷是否已執(zhí)行過,如果已經(jīng)執(zhí)行過,則拋出異常信息,也就是說一次Call實例只能調(diào)用一次execute()方法,和我們之前說的一樣。

如果未執(zhí)行,則調(diào)用Dispatcher類的executed()方法將該Call加入到一個雙端隊列中

private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//雙端隊列  
/** Used by {@code Call#execute} to signal it is in-flight. */  
synchronized void executed(RealCall call) {  
runningSyncCalls.add(call);  
} 

接著調(diào)用getResponseWithInterceptorChain()方法返回Response對象,最后finally中,調(diào)用Dispatcher的finished()方法,在從已經(jīng)執(zhí)行的雙端隊列中移除本次Call。

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();  
}  
}  

通過上面分析,真正發(fā)出網(wǎng)絡(luò)請求,返回結(jié)果應(yīng)該是getResponseWithInterceptorChain()方法,那么接下來,主要看看這個方法

private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
    retryAndFollowUpInterceptor.isForWebSocket()));

Interceptor.Chain chain = new RealInterceptorChain(
    interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

該方法是組裝各種攔截器為一個攔截器鏈,最后調(diào)用RealInterceptorChainproceed()方法,來處理這個請求

@Override public Response proceed(Request request) throws IOException {  
 return proceed(request, streamAllocation, httpStream, connection);  
 }  

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;  
}  

攔截器Interceptor和攔截器鏈Chain都是接口

public interface Interceptor {  
Response intercept(Chain chain) throws IOException;  

interface Chain {  
Request request();  

Response proceed(Request request) throws IOException;  

Connection connection();  
 }  
}  

下面用一張流程圖來說明攔截器鏈遞歸從攔截器中返回Response

下面再看看enqueue()方法(異步方法),指在另外的工作線程中執(zhí)行http請求, 請求時不會阻塞當(dāng)前的線程, 所以可以在Android主線程中使用。

@Override public void enqueue(Callback responseCallback) {  
synchronized (this) {  
if (executed) throw new IllegalStateException("Already Executed");  
executed = true;  
}  
client.dispatcher().enqueue(new AsyncCall(responseCallback));  
}  

首先都校驗這個Call是否已經(jīng)被執(zhí)行,如果執(zhí)行過,就報異常。如果為執(zhí)行,則調(diào)用Dispatcher分發(fā)器的enqueue()方法。看下AsyncCall這個類,

 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);
  }
}
}

NamedRunnable是實現(xiàn)了Runnable接口,AsyncCall–實際上是一個Runnable,在run()方法中調(diào)用了execute()方法,在該方法中調(diào)用getResponseWithInterceptorChain()

Response response = getResponseWithInterceptorChain();  

拿到請求結(jié)果,接下來看一下client.dispatcher().finished(this)做了什么

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();  
 }  
}  

finished()方法先從runningAsyncCalls(異步請求隊列)刪除已經(jīng)執(zhí)行的異步請求,然后接著調(diào)用了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 call = i.next();  

 if (runningCallsForHost(call) < maxRequestsPerHost) {  
   i.remove();  
   runningAsyncCalls.add(call);  
   executorService().execute(call);  
 }  

 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.  
}  
}  

首先runningAsyncCalls(異步請求隊列) readyAsyncCalls和異步調(diào)用準(zhǔn)備任務(wù))兩部判斷,接著循環(huán)readyAsyncCalls),將call加入到runningAsyncCalls中,并在readyAsyncCalls刪除掉該call,接著線程池執(zhí)行call

繼續(xù)看Dispatcher分發(fā)器的enqueue()方法

synchronized void enqueue(AsyncCall call) {  
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {  
 runningAsyncCalls.add(call);  
 executorService().execute(call);  
} else {  
 readyAsyncCalls.add(call);  
}  
}  

如果runningAsyncCalls的大小小于最大請求數(shù)量(最大線程數(shù)量、并發(fā)數(shù)量)并且call小于最大主機請求限
制,那么將call 加入到runningAsyncCalls中,接著線程池執(zhí)行call;否則,將call加入到readyAsyncCalls(異步調(diào)用準(zhǔn)備任務(wù))。
PS: runningCallsForHost()方法,循環(huán)判斷cal的host和runningAsyncCalls的中的call的host相同的數(shù)量。同一請求是否超過想過請求同時存在的最大值

private int runningCallsForHost(AsyncCall call) {  

int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}

接下來看一下Dispatcher類,這個是核心類,Dispatcher是異步請求的策略

public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;

/** Executes calls. Created lazily. */
private ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
}

int maxRequests = 64: 最大并發(fā)請求數(shù)為64
int maxRequestsPerHost = 5: 每個主機最大請求數(shù)為5
Runnable idleCallback:Runnable對象,在刪除任務(wù)時執(zhí)行
ExecutorService executorService:消費者池(也就是線程池)
Deque<AsyncCall> readyAsyncCalls:緩存,異步調(diào)用準(zhǔn)備執(zhí)行任務(wù)
Deque<AsyncCall> runningAsyncCalls:正在運行的異步任務(wù),包含取消尚未完成的調(diào)用
Deque<RealCall> runningSyncCalls: 正在運行的同步任務(wù),包含取消尚未完成的調(diào)用

最后看一下整個請求的流程圖

整個OkHttp源碼分析請求過程就結(jié)束了

總結(jié)

基本上 OkHttp 的請求響應(yīng)的流程就講完了,內(nèi)容有點多。本章節(jié)只是分析了主要部分的源碼,先對整個結(jié)構(gòu)要有一個系統(tǒng)性的了解,還有很多細節(jié)沒有展開,比如連接池、緩存策略等等一些機制的實現(xiàn),有興趣可以單獨深度去了解。


點贊加關(guān)注是給我最大的鼓勵!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容