Okhttp3源碼
hi,大家好,我是愛吃香蕉的猴子,Okhttp3的源碼之前沒有怎深入了解,最近看了很多Okhttp3源碼的文章,看的雲裡霧裡,在這我也不貼連接配接了,整理一篇自己了解的吧。
源碼Githup位址 具體的使用過程上面也有介紹,現在步入正題。
從使用步驟切入,再進入源碼,最後總結
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().
url("https://github.com/square/okhttp").
build();
Call call = client.newCall(request);
try {
Response response = call.execute();
call.enqueue(new Callback() {//異步
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
} catch (IOException e) {
e.printStackTrace();
}
上面的代碼是規規矩矩的使用範例,現在我們逐一解析
- ①OkHttpClient client = new OkHttpClient.Builder().build();
- 第一步聲明client對象,如何聲明呢? 為什麼通過Builder().builder()?
- 那肯定要看OkHttpClient的代碼; path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\OkHttpClient.java
public Builder() {
dispatcher = new Dispatcher();//重點 分析Dispatcher
protocols = DEFAULT_PROTOCOLS;//預設支援的協定
connectionSpecs = DEFAULT_CONNECTION_SPECS;//預設的連接配接規範
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();//預設的代理選擇器,直連
........
- 在這個Builder構造器中,初始化了dispatcher, 在看一下build方法
public OkHttpClient build() {
return new OkHttpClient(this);
}
這樣就聲明了client對象,并初始化了dispatcher等等.
- ②Request request = new Request.Builder().url().build();
- 這一步就是初始化request; 方法和OkhttpClient很一樣, 注釋的大概意思:一個HTTP請求。我了解為對請求連結的處理吧
- 然後主要就是要将request(資料請求連接配接)放入newCall中。
public Builder() {
this.method = "GET"; //get的請求方式
this.headers = new Headers.Builder();
}
- ③Call call = client.newCall(request);
- 上面兩步的執行,就是為第三步做準備的,現在 在okhttpclient中newCall方法聲明了call對象.
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
- Call是一個interface, RealCall是真正的實作類,可以着重看RealCall;
- path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\RealCall.java
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.transmitter = new Transmitter(client, call);
return call;
}
這樣就是初始化了call, 這樣就要開始異步請求的工作;
- ④ call.enqueue(new Callback() {
@Override public void enqueue(Callback responseCallback) {
。。。。
//client.dispatcher()傳回一個Dispatcher對象,異步請求排程器,裡面會對任務進行分辨,是立刻執行還是放入等待隊列
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
- 最後的代碼中參數new AsyncCall(responseCallback); 我們來猜一下 這個類是幹嘛的,我的直覺是和Runnable有關,因為畢竟異步請求嘛
- final class AsyncCall extends NamedRunnable { //果然繼承NameRunnable
- 這樣RealCall的代碼暫時看到這裡,現在看Dispatcher的enqueue方法;
path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\Dispatcher.java
- 先看一些變量
private int maxRequests = 64;//最大并發數為64,同時請求
private int maxRequestsPerHost = 5;//每個主機的最大請求數為5
private @Nullable Runnable idleCallback;//閑置接口
/** Executes calls. Created lazily. */
private @Nullable 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<>();
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);//緩存好的異步調用,都是放在隊列裡儲存
。。。
//TODO
promoteAndExecute();//這一步就是增加線程池
}
進入promoteAndExecute看一下
private boolean promoteAndExecute() {
。。。。。
//如果目前運作的異步請求隊列長度小于最大請求數,也就是64,并且主機的請求數小于每個主機的請求數也就是5,
//則把目前請求添加到 運作隊列,接着交給線程池ExecutorService處理,否則則放置到readAsyncCall進行緩存,等待執行
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
。。。。。
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
//進入線程池
asyncCall.executeOn(executorService());
}
return isRunning;
}
- 網絡請求資料,增加線程池 提高效率,進入線程池看一下–> executorService;
//同步最大的差別
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
- 小結:添加到線程池後就交給ThreadPoolExecutor去調用,最終則是調用到我們的請求AsyncCall的execute方法。回看上面的代碼,異步請求中,我們傳遞了個Callback接口進來,而在RealCall的enqueue方法中,Callback回調接口被封裝到AsyncCall中,而AsyncCall繼承與NamedRunnable,而NamaedRunnable則實作了Runnable方法。
-
現在,傳回到RealCall中檢視executeOn方法;
path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\RealCall.java # 内部類:AsyncCall
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
進入executorService.execute(this);檢視
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
//重點: 看OkHttp如何連接配接傳回
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
現在要對傳回值處理: getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
-
這樣就分析到了,Okhttp的關鍵部分攔截器
使用者自定義連接配接器
RetryAndFollowUpInterceptor:在連接配接失敗後進行重新連接配接,必要時進行重定向,如果調用被取消,可能會抛出IOException
BridgeInterceptor:建構通路網絡的橋梁,首先,将使用者請求轉換成網絡請求,然後通路網絡,最後将網絡響應轉換成使用者響應。
CacheInterceptor:緩存攔截器,從緩存中擷取伺服器請求,或者把伺服器響應寫入緩存中。
ConnectInterceptor:打開一個連接配接,去連接配接目标伺服器。
CallServerInterceptor:攔截器鍊中的最後一個鍊點,通過網絡請求伺服器。