OkHttp3.7源碼分析文章列表如下:
拆輪子:OkHttp 的源碼解析(一):概述
拆輪子:OkHttp 的源碼解析(二):流程分析
拆輪子:OkHttp 的源碼解析(三):任務分發器(Dispatcher)
拆輪子:OkHttp 的源碼解析(四):攔截器(Interceptor)
1、OkHttp 的基本使用
用我們最常用的異步調用來分析:
OkHttpClient okHttpClient = new OkHttpClient(); // 第一行代碼
Request request = new Request.Builder().url("url").build(); // 第二行代碼
Call call = okHttpClient.newCall(request); // 第三行代碼
call.enqueue(new Callback() { // 第四行代碼
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
}
});
2、第一行代碼分析
主要有關的類是 OkHttpClient
:
OkHttpClient okHttpClient = new OkHttpClient();
該代碼作用是生成一個 OkHttpClient 對象,配置一些請求參數。
當我們 new OkHttpClient() 的時候肯定調用構造方法,先看下構造方法:
public OkHttpClient() {
this(new Builder());
}
它這里直接調用的是帶參數的非 public 的構造方法:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
······
}
下面很長不寫了,反正就是配置了一些參數。但是我們把鼠標往上拉,還會看到一串很長的代碼:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
static {
Internal.instance = new Internal() {
@Override public void addLenient(Headers.Builder builder, String line) {
builder.addLenient(line);
}
@Override public void addLenient(Headers.Builder builder, String name, String value) {
builder.addLenient(name, value);
}
······
@Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
return new RealCall(client, originalRequest, true);
}
};
}
我們都知道在 Java 中被 static 修飾的成員變量和成員方法獨立于該類的任何對象,被類的所有實例共享。
上面源碼中為什么要這么做呢?
官方文檔中有這樣一個解釋:
Escalate internal APIs in {@code okhttp3} so they can be used from OkHttp's implementation
packages. The only implementation of this interface is in {@link OkHttpClient}.
這也是一種封裝,Internal 的唯一實現在 OkHttpClient 中,OkHttpClient 通過這種方式暴露其 API 給外部類使用。
OkHttpClient 這個類后面還有用到后面再說,我們先看到這,這行代碼其實很簡單就是生成了一個 OkHttpClient 的對象。
3、第二行代碼分析
主要有關的類是 Request
:
Request request = new Request.Builder().url("url").build();
這行代碼的作用使用鏈式調用生成一個 Request 對象,每一個HTTP請求包含一個URL、一個方法(GET或POST或其他)、一些HTTP頭。請求還可能包含一個特定內容類型的數據類的主體部分。
將其拆分我們一個一個看:
Request.Builder builder = new Request.Builder();
Request.Builder url = builder.url("url");
Request request = url.build();
3.1 對于 new Request.Builder() 分析
Builder 是 Request 的一個內部類。當我們調用:
Request.Builder builder = new Request.Builder();
我們查看源碼看是怎么走的:
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
······
}
從源碼中可以看出,如果我們未設置請求方式,直接調用 Builder 的構造方法時,默認請求方式是 "GET"。
3.2 對于 builder.url("url") 分析
public static class Builder {
······
/**
* Sets the URL target of this request.
*
* @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this
* exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.
*/
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
······
}
從源碼中可以看到,如果我們傳入一個字符串的 url,如果字符串為空會拋出一個空指針異常;不為空則判斷是 http 請求還是 https 請求。
3.3 對于 url.build() 分析
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
此處也做了一個 url 的非空判斷,然后生成一個 Request 對象并將其返回,我們在看下 Request 的構造方法。
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;
}
4、第三行代碼分析
Call call = okHttpClient.newCall(request);
這行代碼使用上面生成的 OkHttpClient 對象來調用它的 newCall 方法,將前面的 Request 請求對象傳入,所有邏輯就在 newCall( ) 方法中。
我們又回到了 OkHttpClient 類:
/**
* 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 和 RealCall,看名字就知道他們兩個有關系。
先看 Call,我刪除所有的注釋,如下:
package okhttp3;
import java.io.IOException;
public interface Call extends Cloneable {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
原來 Call 只是一個接口,Cloneable 這個接口是個空的,顯然 RealCall 是它的具體實現類。
原來我們執行網絡請求的異步 enqueue
和同步 execute
操作都定義在這。
現在跳轉到 RealCall 這個實現類,在上面我們看到傳入了三個參數,點擊去看下:
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
5、最后一行代碼
終于到了最后一行代碼,請求服務器失敗成功和返回值都在這里,又分為同步和異步。
同步請求
call.execute();
點擊 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);
}
}
先判斷請求 Call
是否已經執行了,如果執行了則拋出異常,然后繼續執行, client.dispatcher().executed(this)
點擊進去代碼如下:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看出同步請求就是把生成的 Call
插入同步請求的隊列 runningSyncCalls
中。
Response result = getResponseWithInterceptorChain();
返回的 result 就是網絡請求的返回數據。
最后調用 finishied
方法將 call
從同步隊列中刪除。
異步請求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
}
}
源碼如下:
@Override void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
可以看出,不管是異步還是同步,檢查這個 call
是否已經被執行了,每個 call
只能被執行一次,如果想要一個完全一樣的 call
,可以利用 call#clone
方法進行克隆。
同樣先置標志位,然后將封裝的一個執行體放到異步執行隊列中。這里面引入了一個新的類AsyncCall
,這個類繼承于NamedRunnable
,實現了Runnable
接口。NamedRunnable
可以給當前的線程設置名字,并且用模板方法將線程的執行體放到了execute
方法中。
兩種請求的方法
enqueue/execute
方法中涉及好幾個新的類和方法,如下:
-
同步
execute
:- captureCallStackTrace()
- dispatcher()
- getResponseWithInterceptorChain()
-
異步
enqueue
:- captureCallStackTrace()
- dispatcher()
- AsyncCall
我們看到不管是同步還是異步都會調用兩個相同的方法 captureCallStackTrace()
和 dispatcher()
,先看下 captureCallStackTrace()
源碼:
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
其實,我們根據方法的名字就知道什么意思,Stack:棧,Trace:追蹤 ,是用來跟蹤調用棧的信息的,不去深究了。
對于 dispatcher()
,它涉及到一個新的類 Dispatcher
,這個類有點復雜,它是一個核心重點類。在下篇文章中詳細說。