本篇文章主要介紹OkHttp從發起請求到接收到結果的整個執行流程. 由上篇的概述可知,OkHttp支持同步和異步的請求,這里我們以異步請求為例進行分析
一,異步OkHttp請求示例
private void testOkHttp() throws IOException {
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url("https://www.google.com.hk").build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.toString());
Log.i(TAG,response.body().string());
}
});
}
上面的代碼就實現了一個網絡請求. 創建一個OkHttpClient對象,他包含了分發器,連接池,攔截器等等內容. 接下來創建一個請求對象,通過OkHttpClient的newCall()方法對其包裝,然后添加請求隊列,然后回調結果
Tip
對于OkHttpClient封裝一個單實例,讓所有的Http請求都重用它, OkHttp將會有更好的性能.因為每一個OkHttpClient實例都持有自己的連接池,線程池等,重用連接池和線程池能夠減少網絡訪問延遲以及節省內存,減少資源的浪費
下面我們就詳細的分析一下OkHttp詳細的網絡訪問流程
二,OkHttp詳訪流程
1,創建HttpClient對象.
public OkHttpClient() {
this(new Builder());
}
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.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
... ...
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
由上面可知HttpClient創建的時候,初始化了連接池,線程池,默認攔截器等等,為接下來的網絡請求鋪墊
2,創建Request對象
Request request = new Request.Builder().url("https://www.google.com.hk").build();
通過Request中的Builder構建一個請求對象
這個Request對象我們可以理解成一個請求Bean,里面封裝了請求的url, 請求方法(post get put delete等),請求頭,請求體,以及tag(用于批量取消)
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
接下來繼續對Request對象進行包裝,使其能夠執行execute,入隊enqueue, 取消cancel, 判斷狀態 isCancel和isExecuted
上面的call指向的是RealCall對象
我們看下Call接口
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);
}
}
3,將請求入隊
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
OkHttpClient的dispatcher()方法返回的是一個Dispatcher對象,每個Dispatcher使用線程池來運行上面的Call任務.
接下來我們看下Dispacher的入隊操作
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
runningAsyncCalls存放所有正在執行的異步任務
readyAsyncCalls存放所有正在等待執行的異步任務
當正在執行的請求小于最大請求數64且每個主機的最大請求數不超過5 則直接加入到正在執行的異步隊列中且執行.
否則加入到正在等待執行的異步隊列中等待執行.
**由上我們可知
請求相同主機的最大請求數目是5
所有請求最大數目是64
實際開發中,用戶量一般的app服務器只有一個,那么一般最大的請求就是5
**
好了,一個請求封裝準備了這么多, 順利入隊,下面我們看看他下面是怎么執行的
4,執行
RealCall封裝了原始的Request,在RealCall中有一個非靜態內部類AsyncCall,AsyncCall中的execute方法就是任務要執行的代碼
final class AsyncCall extends NamedRunnable {
... ...
... ...
@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);
}
}
}
通過getResponseWithInterceptorChain方法 獲取到Response對象,下面我們看下他的攔截鏈
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);
return chain.proceed(originalRequest);
}
RealInterceptorChain.java
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {
... ...
//取出下一個攔截器,遞歸調用
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
... ...
return response;
}
1)首先添加用戶自定義的攔截器(如果有),接著依次添加重試攔截器,橋接攔截器,緩存攔截器,連接攔截器以及訪問服務器的攔截器.
這些攔截器的添加順序是固定的,不能改變
2)這些攔截器形成一個攔截鏈,每個攔截器專司其自己的職責, 將處理好的請求交給下一個攔截器,最后一個攔截器處理完畢后,再將其交給上一個攔截器,直到第一個, 最后將結果返回.這樣一個責任鏈就形成了
3)每個攔截器都實現Interceptor接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
4)對于攔截器之間的調用過程可能不太容易理解,下面針對2)給出一個示意圖
Req: 請求Request縮寫
Res: 響應Response縮寫
5,結果回調
我們再將目光回到AsyncCall類
@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);
}
}
}
當通過getResponseWithInterceptorChain()一系列的處理得到結果后.
1),會首先判斷結果是否取消, 如果取消則回調傳入的回調onFail中
否則,回調結果到onResponse中
2),接下來是掃尾操作
調用Dispatcher的finish方法
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
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();
}
}
它主要做了兩件事兒
A,將剛才已經完成的任務從runningAsyncCalls移除
B,如果runningAsyncCall小于5(每個主機的最大訪問數量), 則從readyAsyncCalls移除添加到runningAsyncCalls中,并且執行
至此一個完整的OkHttp異步請求完畢
總的來說: 就是根據請求的url, 請求方法, 請求體構造一個Request的Bean, 然后將Request封裝成一個RealCall,RealCall實現了Call接口,他有對執行邏輯的操作(取消,執行,判斷執行的狀態等), 接下來通過全局的OkHttpClient(擁有線程池,連接池,分發器等資源),利用分發器Dispatcher加入到執行隊列或者等待隊列中.
然后線程池執行RealCall的非靜態內部類AsyncCall的execute方法, 然后通過getResponseWithInterceptorChain來進行一系列的攔截器操作處理,回調請求結果.
最后進行掃尾操作,移除正在執行隊列的當前請求,將等待隊列的請求添加到執行隊列.
OkHttp的處理核心是在攔截器,每個攔截器都有自己的職責,下一篇文章詳細分析各個攔截器具體都干了什么
OkHttp之攔截器(二)