這段時間老李的新公司要更換網絡層,知道現在主流網絡層的模式是RxJava+Retrofit+OKHttp,所以老李開始研究這三個項目的源代碼,在更換網絡層后,開始分享這個三個項目源碼的分析。*
本篇文章 主要講解OKHttp源碼解析(3.7.0)
OKHttp官網
github地址
本文大體上分為11個部分
- 1.OkHttp源碼解析(一):OKHttp初階
- 2 OkHttp源碼解析(二):OkHttp連接的"前戲"——HTTP的那些事
- 3 OkHttp源碼解析(三):OKHttp中階之線程池和消息隊列
- 4 OkHttp源碼解析(四):OKHttp中階之攔截器及調用鏈
- 5 OkHttp源碼解析(五):OKHttp中階之OKio簡介
- 6 OkHttp源碼解析(六):OKHttp中階之緩存基礎
- 7 OkHttp源碼解析(七):OKHttp中階之緩存機制
- 8 OkHttp源碼解析(八):OKHttp中階之連接與請求值前奏
- 9 OkHttp源碼解析(九):OKHTTP連接中三個"核心"RealConnection、ConnectionPool、StreamAllocation
- 10 OkHttp源碼解析(十) OKHTTP中連接與請求
- 11 OkHttp的感謝
本篇文章的主要內容如下:
- OkHttp介紹
- OkHttp使用
- OkHttp流程源碼跟蹤
一、OKHTTP簡介
- 1.支持HTTP2/SPDY
- 2.socket自動選擇最好路線,并支持自動重連
- 3.擁有自動維護的socket連接池,減少握手次數
- 4.擁有隊列線程池,輕松寫并發
- 5.擁有Interceptors輕松處理請求與響應(比如透明GZIP壓縮)基于Headers的緩存策略
二、OKHTTP使用:
1、GET請求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
2、POST請求
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
三、OKHTTP源碼流程分析
(一)、OKHTTP 同步請求debug代碼跟蹤:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
從上面代碼所示,先是new了一個OKHttpClient對象。
1、OKHttpClient類詳解
OKHttpClient類就比較簡單了:
- 1、里面包含了很多對象,其實OKhttp的很多功能模塊都包裝進這個類,讓這個類單獨提供對外的API,這種外觀模式的設計十分的優雅。外觀模式。
- 2、而內部模塊比較多,就使用了Builder模式(建造器模式)。Builder模式(建造器模式)
- 3、它的方法只有一個:newCall.返回一個Call對象(一個準備好了的可以執行和取消的請求)。
而大家仔細讀源碼又會發現構造了OKHttpClient后又new了一個Rquest對象。那么咱們就來看下Request,說道Request又不得不提Response。所以咱們一起講了
2、Request、Response類詳解
- 1、Request、Response分別抽象成請求和相應
- 2、其中Request包括Headers和RequestBody,而RequestBody是abstract的,他的子類是有FormBody (表單提交的)和 MultipartBody(文件上傳),分別對應了兩種不同的MIME類型
FormBody :"application/x-www-form-urlencoded"
MultipartBody:"multipart/"+xxx.- 3、其中Response包括Headers和RequestBody,而ResponseBody是abstract的,所以他的子類也是有兩個:RealResponseBody和CacheResponseBody,分別代表真實響應和緩存響應。
- 4、由于RFC協議規定,所以所有的頭部信息不是隨便寫的,request的header與response的header的標準都不同。具體的見 List of HTTP header fields。OKHttp的封裝類Request和Response為了應用程序編程方便,會把一些常用的Header信息專門提取出來,作為局部變量。比如contentType,contentLength,code,message,cacheControl,tag...它們其實都是以name-value對的形勢,存儲在網絡請求的頭部信息中。
根據從上面的GET請求,顯示用builder構建了Request對象,然后執行了OKHttpClient.java的newCall方法,那么咱們就看看這個newCall里面都做什么操作?
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
Call是個什么東西,那咱們看下Call這個類
3、Call類詳解
Call: HTTP請求任務封裝
可以說我們能用到的操縱基本上都定義在這個接口里面了,所以也可以說這個類是OKHttp類的核心類了。我們可以通過Call對象來操作請求了。而Call接口內部提供了Factory工廠方法模式(將對象的創建延遲到工廠類的子類去進行,從而實現動態配置)
Call接口提供了內部接口Factory(用于將對象的創建延遲到該工廠類的子類中進行,從而實現動態的配置).
/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*/
public interface Call extends Cloneable {
/** Returns the original request that initiated this call. */
Request request();
/**
* Invokes the request immediately, and blocks until the response can be processed or is in
* error.
*
* <p>To avoid leaking resources callers should close the {@link Response} which in turn will
* close the underlying {@link ResponseBody}.
*
* <pre>@{code
*
* // ensure the response (and underlying response body) is closed
* try (Response response = client.newCall(request).execute()) {
* ...
* }
*
* }</pre>
*
* <p>The caller may read the response body with the response's {@link Response#body} method. To
* avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
* Response.
*
* <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
* not necessarily indicate application-layer success: {@code response} may still indicate an
* unhappy HTTP response code like 404 or 500.
*
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws IllegalStateException when the call has already been executed.
*/
Response execute() throws IOException;
/**
* Schedules the request to be executed at some point in the future.
*
* <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
* immediately unless there are several other requests currently being executed.
*
* <p>This client will later call back {@code responseCallback} with either an HTTP response or a
* failure exception.
*
* @throws IllegalStateException when the call has already been executed.
*/
void enqueue(Callback responseCallback);
/** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
void cancel();
/**
* Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
* #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
*/
boolean isExecuted();
boolean isCanceled();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call clone();
interface Factory {
Call newCall(Request request);
}
}
在源碼中,OKHttpClient實現了Call.Factory接口,返回了一個RealCall對象。那我們就來看下RealCall這個類
4、RealCall類詳解
- 1、OkHttpClient的newCall方法里面new了RealCall的對象,但是RealCall的構造函數需要傳入一個OKHttpClient對象和Request對象(PS:第三個參數false表示不是webSokcet).因此RealCall包裝了Request對象。所以RealCall可以很方便地使用這兩個對象。
- 2、RealCall里面的兩個關鍵方法是:execute 和 enqueue。分別用于同步和異步得執行網絡請求。
- 3、RealCall還有一個重要方法是:getResponseWithInterceptorChain,添加攔截器,通過攔截器可以將一個流式工作分解為可配置的分段流程,既增加了靈活性也實現了解耦,關鍵還可以自有配置,非常完美。
所以client.newCall(request).execute();實際上執行的是RealCall的execute方法,現在咱們再回來看下RealCall的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);
}
}
首先是
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
判斷call是否執行過,可以看出每個Call對象只能使用一次原則。然后調用了captureCallStackTrace()方法。
RealCall.java
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
RealCall的captureCallStackTrace() 又調用了Platform.get().getStackTraceForCloseable()
public class Platform {
public static Platform get() {
return PLATFORM;
}
/**
* Returns an object that holds a stack trace created at the moment this method is executed. This
* should be used specifically for {@link java.io.Closeable} objects and in conjunction with
* {@link #logCloseableLeak(String, Object)}.
*/
public Object getStackTraceForCloseable(String closer) {
if (logger.isLoggable(Level.FINE)) {
return new Throwable(closer); // These are expensive to allocate.
}
return null;
}
}
其實是調用AndroidPlatform. getStackTraceForCloseable(String closer)方法。這里就不詳細說了,后面詳細說。
然后retryAndFollowUpInterceptor.setCallStackTrace(),在這個方法里面什么都沒做就是set一個object進去
public final class RetryAndFollowUpInterceptor implements Interceptor {
public void setCallStackTrace(Object callStackTrace) {
this.callStackTrace = callStackTrace;
}
}
綜上所示captureCallStackTrace()這個方法其實是捕獲了這個請求的StackTrace。
然后進入了第一個核心類---Dispatcher的的execute方法了,由于下面是進入了關鍵部分,所以重點講解下,代碼如何:
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
看下OKHttpClient的dispatcher()方法的具體內容如下圖
//OKHttpClient.java
public Dispatcher dispatcher() {
return dispatcher;
}
大家發現client.dispatcher()返回的是Dispatcher對象,那么這個Dispatcher對象是何時創建的那?在OkHttpClient.java里面Build類里面的構造函數里面,如下圖
//OkHttpClient.java
public static final class Builder {
//其它代碼先忽略掉
public Builder() {
dispatcher = new Dispatcher();
//其它代碼先忽略掉
}
}
所以默認執行Builder()放到時候就創建了一個Dispatcher。那么咱們看下dispatcher里面的execute()是如何處理的
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
里面發現是runningSyncCalls執行了add方法莫非runningSyncCalls是個list,咱們查看dispatcher里面怎么定義runningSyncCalls的。
/** 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<>();
原來runningSyncCalls是雙向隊列啊,突然發現Dispatcher里面定義了三個雙向隊列,看下注釋,我們大概能明白readyAsyncCalls 是一個存放了等待執行任務Call的雙向隊列,runningAsyncCalls是一個存放異步請求任務Call的雙向任務隊列,runningSyncCalls是一個存放同步請求的雙向隊列。關于隊列咱們在下篇文章里面詳細介紹。
執行完client.dispatcher().executed(this);要走到getResponseWithInterceptorChain();方法了里面了,看下這個方法是具體做什么的?
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//添加開發者應用層自定義的Interceptor
interceptors.addAll(client.interceptors());
//這個Interceptor是處理請求失敗的重試,重定向
interceptors.add(retryAndFollowUpInterceptor);
//這個Interceptor工作是添加一些請求的頭部或其他信息
//并對返回的Response做一些友好的處理(有一些信息你可能并不需要)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//這個Interceptor的職責是判斷緩存是否存在,讀取緩存,更新緩存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//這個Interceptor的職責是建立客戶端和服務器的連接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//添加開發者自定義的網絡層攔截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
//一個包裹這request的chain
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//把chain傳遞到第一個Interceptor手中
return chain.proceed(originalRequest);
}
發現 new了一個ArrayList,然后就是不斷的add,后面 new了 RealInterceptorChain對象,最后調用了chain.proceed()方法。先看下RealInterceptorChain的構造函數。
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
發現什么都沒做就是做了賦值操作,后面跟蹤下chain.proceed()方法
由于Interceptor是個接口,所以應該是具體實現類RealInterceptorChain的proceed實現
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
public final class RealInterceptorChain implements Interceptor.Chain{
Response intercept(Chain chain) throws IOException;
@Override
public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec, 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 (httpCodec != 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對象時候httpCodec直接賦予了null,所以下面代碼直接略過。
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
然后看到在proceed方面里面又new了一個RealInterceptorChain類的next對象,溫馨提示下,里面的streamAllocation, httpCodec, connection都是null,所以這個next對象和chain最大的區別就是index屬性值不同chain是0.而next是1,然后取interceptors下標為1的對象的interceptor。由從上文可知,如果沒有開發者自定義的Interceptor時,首先調用的RetryAndFollowUpInterceptor,如果有開發者自己定義的interceptor則調用開發者interceptor。
這里重點說一下,由于后面的interceptor比較多,且涉及的也是重要的部分,而咱們這里主要是講流程,所以這里就不詳細和大家說了,由后面再詳細講解,后面的流程是在每一個interceptor的intercept方法里面都會調用chain.proceed()從而調用下一個interceptor的intercept(next)方法,這樣就可以實現遍歷getResponseWithInterceptorChain里面interceptors的item,實現遍歷循環,縮減后的代碼如下:
//RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代碼
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
//忽略部分代碼
}
//BridgeInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代碼
Response networkResponse = chain.proceed(requestBuilder.build());
//忽略部分代碼
}
//CacheInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代碼
networkResponse = chain.proceed(networkRequest);
//忽略部分代碼
}
//ConnectInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代碼
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
讀過源碼我們知道getResponseWithInterceptorChain里面interceptors的最后一個item是CallServerInterceptor.java,最后一個Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是進行繼續遞歸,具體里面是通過OKio實現的,具體代碼,等后面再詳細說明,CallServerInterceptor返回response后返回給上一個interceptor,一般是開發者自己定義的networkInterceptor,然后開發者自己的networkInterceptor把他的response返回給前一個interceptor,依次以此類推返回給第一個interceptor,這時候又回到了realCall里面的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返回給get請求的返回值。至此整體GET請求的大體流程就已經結束了。(PS:最后別忘記走client.dispatcher().finished(this))
大體流程如下圖:
(二)、OKHTTP 異步請求debug代碼跟蹤:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
前面和同步一樣new了一個OKHttp和Request。這塊和同步一樣就不說了,那么說說和同步不一樣的地方,后面異步進入enqueue()方法
//RealCall.java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
由于executed默認為false,所以先進行判斷是否為true,為true則直接跑異常,沒有則設置為true,可以看出executed這個是一個標志,標志這個請求是否已經正在請求中,合同步一樣先調用了captureCallStackTrace();然后調用 client.dispatcher().enqueue(new AsyncCall(responseCallback));client.dispatcher()返回的是Dispatcher對象所以實際調用的是Dispatcher的enqueue(),那么咱們進入源碼看下
//Dispatcher.java
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
//如果正在執行的請求小于設定值即64,并且請求同一個主機的request小于設定值即5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加到執行隊列,開始執行請求
runningAsyncCalls.add(call);
//獲得當前線程池,沒有則創建一個
executorService().execute(call);
} else {
//添加到等待隊列中
readyAsyncCalls.add(call);
}
}
根據源碼和注釋大家可以看到如果正在執行的異步請求小于64,并且請求同一個主機小于5的時候就先往正在運行的隊列里面添加這個call,然后用線程池去執行這個call,否則就把他放到等待隊列里面。執行這個call的時候,自然會去走到這個call的run方法,那么咱們看下AsyncCall.java這個類,而AsyncCall.java又繼承自NamedRunnable.java咱們就一起看下他們的源碼
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
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的構造方法設置了name在的run方法里面設定為當前線程的name,而NamedRunnable的run方法里面調用了它自己的抽象方法execute,由此可見NamedRunnable的作用就是設置了線程的name,然后回調子類的execute方法,那么我們來看下AsyncCall的execute方法。貌似好像又回到了之前同步的getResponseWithInterceptorChain()里面,根據返回的response來這只callback回調。所以我們得到了OKHTTP的大體流程,如下圖:
三、OKHTTP類詳解
大體核心類主要下圖:
最后給大家看一下整體的流程圖