Okhttp解析
總體思想
分析源碼,首先要熟悉用例澈圈,由上到下一層一層剝開源碼它掂,初步了解項(xiàng)目的框架巴帮,然后再細(xì)看代碼的實(shí)現(xiàn)細(xì)節(jié)∷萜現(xiàn)在試著分析 OKhttp 的源碼,下面代碼是來至 OKhttp官網(wǎng)榕茧。
GET A URL
OkHttpClient client = new OkHttpClient(); //(1)
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build(); //(2)
Response response = client.newCall(request).execute(); //(3)
return response.body().string(); //(4)
}
- 創(chuàng)建一個 OkHttpClient 對象垃沦。
- 創(chuàng)建一個 Request 對象,可以設(shè)置 URL 等網(wǎng)絡(luò)配置用押。
- 調(diào)用 OkHttpClient 的 newCall() 方法肢簿,并把自定義配置的Request對象作為參數(shù)傳進(jìn)去。到處為止程序成功的將需要的請求放在了隊(duì)列中蜻拨,執(zhí)行 execute() 方法開始向服務(wù)器發(fā)起請求池充,服務(wù)器返回的信息轉(zhuǎn)化為 Response 對象。
- 返回 response 對象的 body 主體信息缎讼。
一張來至piasy的流程圖:
細(xì)節(jié)分析
創(chuàng)建 OkHttpClient 對象
OkHttpClient client = new OkHttpClient();
OkHttpClient.class 里面的兩個構(gòu)造函數(shù):
public OkHttpClient() {
this(new Builder());
}
private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
.
.
.
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
}
上面可以看出收夸,new OkHttpClient() 是調(diào)用 OkHttpClient.class 另外一個 private 的構(gòu)造函數(shù) OkHttpClient(Builder builder) ,其中 Builder 是 OkHttpClient 的一個內(nèi)部類休涤,Builder 是使用了構(gòu)造者模式咱圆,里面包含了一些配置相關(guān)的字段如下:
final Dispatcher dispatcher; //分發(fā)器
final Proxy proxy; //代理
final List<Protocol> protocols; //協(xié)議
final List<ConnectionSpec> connectionSpecs; //傳輸層版本和連接協(xié)議
final List<Interceptor> interceptors; //攔截器
final List<Interceptor> networkInterceptors; //網(wǎng)絡(luò)攔截器
.
.
.
final int readTimeout; //read 超時(shí)
final int writeTimeout; //write 超時(shí)
創(chuàng)建 Request 對象
創(chuàng)建完 OkHttpClient 對象后笛辟,就需要我們創(chuàng)建一個Request功氨,Request 作用就是承載用戶的請求,最簡單的也是必須的做法是設(shè)置 Request 的 URL手幢。同樣和創(chuàng)建 OkHttpClient 對象一樣捷凄,Request 也是使用 構(gòu)造者模式,其中包含了 URL, header, body等字段围来,簡單看看源碼中的 Request 的構(gòu)造函數(shù):
private 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;
}
真正開始工作啦
同步請求
最上面的官方示例代碼是一個同步的網(wǎng)絡(luò)請求跺涤,接著我們一步一步拆解代碼。
//官方示例代碼
Response response = client.newCall(request).execute();
//OkHttpClient.class
public Call newCall(Request request) {
return new RealCall(this, request);
}
//Recall.class
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
}
@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);
}
}
在 OkhttpClient 代碼里面看到监透,newCall() 函數(shù)返回一個 Call 對象桶错,其實(shí) Call 是一個接口,而我們的沒一次請求都是轉(zhuǎn)載在一個 Call 對象中胀蛮。其中看到返回的是一個 RealCall 對象院刁,由這里看到 RealCall 是 Call 的一個實(shí)現(xiàn)類。
Ok粪狼,接著看看 RealCall 對象的 execute() 方法退腥,可以看到這樣一行最重要的代碼 Response response = getResponseWithInterceptorChain() ,通過這一個方法可得到從服務(wù)器返回的一個 Response 對象再榄。從這個函數(shù)的名字可以推測狡刘,這是一個接一個的鏈?zhǔn)秸{(diào)用,接下來也可以從源碼發(fā)現(xiàn)困鸥,這個地方使用了責(zé)任鏈模式
//RealCall.call
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
上的代碼總的功能就是往一個 list 里面一個一個的添加 interceptor 嗅蔬,interceptor 是一個抽象的接口,而代碼里添加的各種各樣的攔截器都是對 interceptor 接口不同的實(shí)現(xiàn)。例如购城,首先添加用戶自己配置的interceptor吕座,然后添加 retryAndFollowUpInterceptor 主要負(fù)責(zé)重定向和失敗重連,接著添加 BridgeInterceptor 主要負(fù)責(zé)轉(zhuǎn)化用戶配置的url瘪板,header等配置生成一個服務(wù)器能接受的文本格式吴趴。列表中 CacheInterceptor 我們大概能猜出它的作用,這是一個緩存攔截器侮攀,根據(jù) url 讀取緩存中的數(shù)據(jù)锣枝,如果有結(jié)果就在這里砍斷鏈?zhǔn)秸{(diào)用,成功返回結(jié)果兰英。 ConnectInterceptor 則是開始向服務(wù)器發(fā)起連接撇叁。CallServerInterceptor 是正式與服務(wù)器發(fā)生關(guān)系,也是從這個攔截器中返回最終的 Response 結(jié)果畦贸。
當(dāng)然陨闹,要發(fā)生上述的所有動作必須有一個起點(diǎn),chain.proceed(originalRequest) 就是整個鏈路的入口薄坏,就在這里開始一個環(huán)節(jié)扣著一個環(huán)節(jié)執(zhí)行下去趋厉。放回最終的結(jié)果,這不胶坠,一次完整的同步網(wǎng)絡(luò)請求就完成了君账。
異步請求
上面是一個同步請求的解析,現(xiàn)在來談?wù)劗惒秸埱笊蛏疲秸埱笥挟惽ぶ钕缡蟛糠值牧鞒虒?shí)現(xiàn)還是沿用同一份代碼,最大的不同是異步請求加入了 dispatcher 闻牡,見面知其意净赴,就是由這個類來負(fù)責(zé)分發(fā)用戶的請求。按照上面的思路罩润,首先看看一個異步的示例
//異步示例
OkHttpClient client=new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request)
.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
}
@Override
public void onFailure(Call call, IOException e) {
}
});
Request 和 OkHttpClient 創(chuàng)建和同步請求一致玖翅,重點(diǎn)在與 RealCall() 接口里面的 enqueue(CallBack callback) 方法。傳入的當(dāng)然是一個 CallBack 接口對象用戶需要服務(wù)器返回的消息通過這個對象傳遞回來哨啃。復(fù)寫的 onResponse() 成功獲取服務(wù)器端返回的結(jié)果烧栋,onFailure() 返回錯誤失敗的信息,下面接著看 enqueued() 調(diào)用棧拳球。
//RealCall.class
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
//RealCall 內(nèi)部類繼承NamedRunnable(代碼向下滑)
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl().toString());
this.responseCallback = responseCallback;
}
@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);
}
}
}
//Dispatcher.class
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
//NamedRunnable.class
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();
}
RealCall 類 enqueue(Callback responseCallback) 中可以看到 dispatcher() 方法 审姓,dispatcher 也有一個 enqueue(AsyncCall call) 方法, 在這個 enqueue 方法里有一個列表并且執(zhí)行 AsynCall 祝峻,如何執(zhí)行 AsynCall 呢? AsynCall 是 RealCall 的內(nèi)部類魔吐,集成 NamedRunnable扎筒,NamedRunnable 繼承 Runanble。最終的與服務(wù)器發(fā)生交互的動作就在 AsynCall 的 execute() 方法內(nèi)酬姆,這里就看到了熟悉的一行代碼 :
Response response = getResponseWithInterceptorChain();
執(zhí)行的流程就和同步的請求一模一樣嗜桌,不同點(diǎn)是再取得的服務(wù)器最終結(jié)果 Callback 接口放回給用戶。
總結(jié)
更好讀懂 OKHttp 的源碼關(guān)鍵是要了解常用的設(shè)計(jì)模式辞色,用構(gòu)造模式創(chuàng)建出 OkHttpClient 和 Request 對象骨宠,使用責(zé)任鏈模式完成一個鏈?zhǔn)降恼{(diào)用。個人認(rèn)為這就是 OkHttp 最基本的框架相满。代碼的還有許多設(shè)計(jì)精良的部分层亿,還未還好細(xì)讀,目前還不把每一部分做到庖丁解牛的境界立美,有時(shí)間再一另幫 Blog 做另外的分析匿又。