基本用法
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.post()
.build();
Call call = okHttpClient.newCall(request);
//異步請求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: 異步請求失敗 ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: 異步請求成功 " + response.body().string());
}
});
//同步請求
Response response = client.newCall(request).execute();//得到Response 對象(會阻塞當(dāng)前線程)
跟到深處會發(fā)現(xiàn)無論同步和異步請求的關(guān)鍵都是攔截器方法
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);
}
接下來就以該方法為入口分析下OkHttp3的代碼
攔截器調(diào)用流程
再貼貼上面的代碼分析一下
//RealCall.Java
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()));//橋攔截器?主要是用來加Header信息
interceptors.add(new CacheInterceptor(client.internalCache()));//緩存攔截器
interceptors.add(new ConnectInterceptor(client));//連接攔截器,用于建立連接
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());//僅在建立WebSocket時才會加的攔截器
}
interceptors.add(new CallServerInterceptor(forWebSocket));//發(fā)送信息攔截器(之前的連接攔截器負(fù)責(zé)建立連接但是啥也沒干,這個攔截器負(fù)責(zé)給服務(wù)器發(fā)消息并接收返回)
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);//開啟鏈?zhǔn)秸{(diào)用
}
這里主要是攔截器的大概調(diào)用流程,首先添加自定義的攔截器(一般是日志打印攔截器或者再增加Header信息之類的),然后添加其他固定的攔截器,注意這里的攔截器順序都是固定的,添加后用一個RealInterceptorChain包裝并依次調(diào)用,執(zhí)行順序就是添加攔截器的順序,這里每個的攔截器都是繼承了Interceptor接口
public interface Interceptor {
//攔截器子類 需復(fù)寫的方法,基本每個攔截器的具體用法都在這個函數(shù)里面
Response intercept(Chain chain) throws IOException;
//攔截器調(diào)用鏈繼承接口
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
再看看Chain子類RealInterceptorChain的關(guān)鍵方法proceed
//帶Response返回,方便做二次處理
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
//這里僅貼出關(guān)鍵代碼,該函數(shù)其他代碼都是條件判斷和返回處理
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
.........
// 調(diào)用核心:直接index+1其他參數(shù)不變來調(diào)用下一個Interceptor
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
...........
return response;
}
由此看出該方法主要是負(fù)責(zé)調(diào)用鏈的運(yùn)轉(zhuǎn),當(dāng)某個Interceptor在處理好大部分工作(注意這里說大部分是因?yàn)橥ㄟ^后面的Interceptor返回可以再做第二次處理,類似與Android View的觸摸事件下發(fā))后直接把原來的request丟過去調(diào)用chain.proceed(request),RealInterceptorChain類就會自動通過index+1來調(diào)用下一個Interceptor
源葫。這里也可以看出雖然方法內(nèi)一直next next next的 又有Chain這樣的字眼讓人誤以為內(nèi)部是鏈表結(jié)構(gòu)存放Interceptor,其實(shí)是通過列表存放的
這里順帶科普一下列表和鏈表的大概優(yōu)缺點(diǎn)(給自己看。胁镐。。诸衔。)
https://blog.csdn.net/st1441517927/article/details/99483738
總結(jié)
OkHttp通過攔截器設(shè)計模式非常的巧妙,一來是細(xì)化各個分功能解耦度高,層次分明,二來是方便定制(可以自定義攔截器), 再者采用類似Android 觸摸事件分發(fā)的機(jī)制,使得每個Interceptor可以進(jìn)行二次處理,非常關(guān)鍵,某些需要二次處理(或重點(diǎn)處理返回那一次)的就得益于這種機(jī)制,例如重定向攔截器和自定義的日志攔截器尤為明顯