Retrofit,OkHttp,Okio Square 安卓平臺網絡層三板斧源碼學習
基于 okhttp 3.9.0 版本 okhttp github 地址
使用方式
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
- 構造一個 OkHttpClient
- 構造一個 Request
- 調用 OkHttpClient.newCall(Request request) 獲得一個 Call 對象
- 執行 Call.execute() 獲得 Response 對象
- 通過 Response.body() 獲得 ResponseBody 對象
OkHttpClient 創建 http 請求源碼分析。
OkHttpClient 和 OkHttpClient.Builder
OkHttpClient 對象的創建使用了『建造者模式』
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
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;
pingInterval = 0;
}
OkHttpClient.Builder 主要用來設置超時時間、代理、緩存、攔截器等。
然后調用
public OkHttpClient build() {
return new OkHttpClient(this);
}
創建 OkHttpClient
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
……
}
Request 和 Request.Builder
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;
}
Request 主要為了設置 url 、請求方法(GET、POST等)、headers、請求體。
其中有個 tag 比較特殊
/**
* Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
* is unspecified or null, the request is canceled by using the request itself as the tag.
*/
public Builder tag(Object tag) {
this.tag = tag;
return this;
}
根據注釋可以看出 tag 主要用來取消請求。
如果發起 POST 請求,需要使用一個 RequestBody
RequestBody 主要用來設置不同的 POST 請求內容(字節流、文件、字符串)
分析 Call 對象
client.newCall(request)
OkHttpClient 的 newCall
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
發現請求代理給了 RealCall
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;
}
看下 RealCall 的構造函數
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
獲得 Call.execute() 和 Call.enqueue(Callback responseCallback)
Call.execute() 負責同步請求。
Call.enqueue(Callback responseCallback) 負責異步請求。
@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);
}
}
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
從上面代碼可以看出 Call 對象會交給 Dispatcher 對象進行管理。
executed() 方法會把 Call 對象存放在 runningSyncCalls 隊列
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
enqueue() 方法會把 Call 對象存放在 runningAsyncCalls 隊列,如果隊列已滿則會被存放在 readyAsyncCalls 隊列
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
然后會執行到 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));
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);
}
這里可以看到添加了一些列的 Interceptor 對象。這些 intercept 分別負責網絡請求、緩存、壓縮等功能。
而這些 intercept 組合的方式就是『責任鏈模式』,而最后一個 CallServerInterceptor 會真正發起網絡請求。
1. 首先會創建一個 RealInterceptorChain ,傳入所有的 Interceptor,index = 0
2. 然后執行 RealInterceptorChain.proceed(Request request)
3. 再調用 RealInterceptorChain.proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection)
4. 創建一個新的 RealInterceptorChain ,傳入 index 值 +1,對象名稱為 next
5. 獲取 interceptors 中 index 位置的 Interceptor,調用 Interceptor.intercept(next)
6. 在 interceptors 中添加的各種 Interceptor 的 intercept 中都會如下
public Response intercept(Chain chain) throws IOException {
……
chain.proceed(requestBuilder.build());
// or
realChain.proceed(request, streamAllocation, null, null);
……
}
7. 其中 Chain.proceed() 方法又會重復執行 3、4、5、6 步驟,直到所有的 interceptors 被遍歷。
8. 最后添加的 ConnectInterceptor 和 CallServerInterceptor 是發起網絡請求的關鍵
總結以上流程如下
發起網絡請求
先看 ConnectInterceptor
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
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 對象由 RetryAndFollowUpInterceptor 創建并傳入到『責任鏈』中。
public Response intercept(Chain chain) throws IOException {
……
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
……
response = realChain.proceed(request, streamAllocation, null, null);
……
}
}
可以看出 ConnectInterceptor 主要作用就是通過 StreamAllocation 創建了一個 HttpCodec。
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 existingConnection = connection;
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
if (existingConnection != connection) {
eventListener.connectionAcquired(call, connection);
}
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
通過上面代碼我們可以看出雖然返回的只是一個 HttpCodec 但是還會創建一個 RealConnection 。而 RealConnection 則是負責連接服務器發送請求的類。
findHealthyConnection() 方法會調用 findConnection() 方法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
……
RealConnection result;
……
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(
connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
……
}
并且調用 RealConnection 的 connect() 方法進行連接
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled, Call call, EventListener eventListener) {
……
connectSocket(connectTimeout, readTimeout, call, eventListener);
……
}
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
}
……
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} ……
}
這里可以看出 connect() 方法會簡歷一個 Socket 連接,并把 Socket 的輸入/輸出流交包裝成 Okio 的 Source 和 Sink 對象。
然后到 CallServerInterceptor 中
public Response intercept(Chain chain) throws IOException {
……
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
……
//寫入 request body
……
}
httpCodec.finishRequest(); // 通過 Socket OutputStream 發送請求
……
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();
……
return response;
}
總計流程圖如下