前言:
本文也做了一次標(biāo)題黨歌憨,哈哈着憨,其實寫的還是很水,各位原諒我O(∩_∩)O务嫡。
介于自己的網(wǎng)絡(luò)方面知識爛的一塌糊涂甲抖,所以準(zhǔn)備寫相關(guān)網(wǎng)絡(luò)的文章漆改,但是考慮全部寫在一篇太長了,所以分開寫准谚,希望大家能仔細(xì)看挫剑,最好可以指出我的錯誤,讓我也能糾正柱衔。
1.講解相關(guān)的整個網(wǎng)絡(luò)體系結(jié)構(gòu):
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(1)之網(wǎng)絡(luò)體系結(jié)構(gòu)
2.講解相關(guān)網(wǎng)絡(luò)的重要知識點樊破,比如很多人都聽過相關(guān)網(wǎng)絡(luò)方面的名詞,但是僅限于聽過而已唆铐,什么tcp ,udp ,socket ,websocket, http ,https ,然后webservice是啥哲戚,跟websocket很像,socket和websocket啥關(guān)系長的也很像艾岂,session,token,cookie又是啥顺少。
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(2)之TCP/UDP
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(3)之HTTP/HTTPS
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(4)之socket/websocket/webservice
相關(guān)網(wǎng)絡(luò)知識點小結(jié)- cookie/session/token(待寫)
3.相關(guān)的第三方框架的源碼解析,畢竟現(xiàn)在面試個大點的公司澳盐,okhttp和retrofit源碼是必問的祈纯。
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(6)之 OkHttp超超超超超超超詳細(xì)解析
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(7)之 Retrofit源碼詳細(xì)解析
這里提一個本文無關(guān)的小知識點,很多文章開頭都會提到叼耙,我們以okhttp3.xxx版本來講解,那怎么看當(dāng)前最新的已經(jīng)是幾了呢粒没?(主要以前也有人問過我在哪里查看xxx第三方庫最新的版本筛婉,所以想到提一下這個)其實很簡單,我們以okhttp為例:
-
Android Studio直接查看:
- JCenter上查看:
JCenter上搜索Okhttp版本
- Maven上查看:
Maven上搜索Okhttp版本
- ........其他方式
正文
看不清楚的癞松,可以右鍵爽撒,選擇新標(biāo)簽頁中打開,然后點擊圖片放大
首先我們來確定總體大綱:
- okhttp相關(guān)參數(shù)配置响蓉,比如設(shè)置超時時間硕勿,網(wǎng)絡(luò)路徑等等等等等.......
- 我們知道在使用okhttp的時候可以使用同步請求,也可以使用異步請求枫甲,所以肯定不同的請求源武,在分發(fā)的時候有不同的處理。
- 我們以前網(wǎng)絡(luò)系列的文章提過想幻,發(fā)送到后臺粱栖,肯定是一個完整的請求包,但是我們使用okhttp的時候脏毯,只是轉(zhuǎn)入了我們需要給后臺的參數(shù)闹究,甚至我們?nèi)绻莋et請求,只是傳入了相應(yīng)的url網(wǎng)絡(luò)地址就能拿到數(shù)據(jù)食店,說明okhttp幫我們把簡單的參數(shù)輸入渣淤,然后通過一系列的添加封裝赏寇,然后變成一個完整的網(wǎng)絡(luò)請求包出去,然后我們在使用okhttp的時候价认,拿到返回的數(shù)據(jù)也已經(jīng)是我們可以直接用的對象蹋订,說明接受的時候,已經(jīng)幫我們把拿到的返回網(wǎng)絡(luò)包刻伊,解析成我們直接用的對象了露戒。<font color = "red">所以在一系列幫我們發(fā)送的時候添加參數(shù)變成完整網(wǎng)絡(luò)請求包,收到時候幫我們解析返回請求包的過程捶箱,是Okhttp的一個個攔截器們所處理智什,它攔截到我們的數(shù)據(jù),然后進(jìn)行處理丁屎,比如添加一些數(shù)據(jù)荠锭,變成完整的網(wǎng)絡(luò)請求包等操作</font>。
所以我們大概就知道了okhttp一般的主要內(nèi)容為這三大塊晨川。
1.okhttp基礎(chǔ)使用:
講解源碼前证九,先寫上okhttp基本使用,這樣才更方便講解源碼:
String url = "http://www.baidu.com";
//'1. 生成OkHttpClient實例對象'
OkHttpClient okHttpClient = new OkHttpClient();
//'2. 生成Request對象'
Request request = new Request.Builder().url(url).build();
//'3. 生成Call對象'
Call call = okHttpClient.newCall(request);
//'4. 如果要執(zhí)行同步請求:'
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
//'5. 如果要執(zhí)行異步請求:'
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
2. 初始化相關(guān)參數(shù)解析:
我們來看我們最剛開始的完整流程圖:
然后配合上面第一步的okhttp基本使用共虑,發(fā)現(xiàn)在執(zhí)行同步和異步前愧怜,我們要先準(zhǔn)備好OkhttpClient
、Request
妈拌、Call
對象拥坛。我們一步步來看相關(guān)源碼:
2.1 OkHttpClient相關(guān):
我們上面的代碼實例化OkHttpClient對象的代碼是:
OkHttpClient okHttpClient = new OkHttpClient();
我們進(jìn)入查看:
發(fā)現(xiàn)
OkHttpClient
除了空參數(shù)的構(gòu)造函數(shù),還有一個傳入Builder
的構(gòu)造函數(shù)尘分,而我們的new OkHttpClient()
最終也是調(diào)用了傳入Builder
的構(gòu)造函數(shù)猜惋,只不過傳入默認(rèn)的Builder
對象值,如下圖所示:我們可以看到最后幾個值:
......
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
......
默認(rèn)的連接超時培愁,讀取超時著摔,寫入超時,都為10秒定续,然后還有其他等默認(rèn)屬性谍咆,那我們加入想要改變這些屬性值呢,比如超時時間改為20秒香罐,很簡單卧波。我們不使用默認(rèn)的Builder
對象即可:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(20,TimeUnit.SECONDS);
builder.readTimeout(20,TimeUnit.SECONDS);
builder.writeTimeout(20,TimeUnit.SECONDS);
OkHttpClient okHttpClient = builder.build();
//這里不能直接使用那個傳入Builder對象的OkHttpClient的構(gòu)造函數(shù),因為該構(gòu)造函數(shù)的方法不是public的
OkHttpClient okHttpClient = new OkHttpClient(builder);//這樣是錯誤的
builder.build();的源碼是:
public OkHttpClient build() {
return new OkHttpClient(this);
}
我們再回過頭來看看OkHttpClient里面設(shè)置的屬性值都有什么用:
Dispatch:分發(fā)器庇茫,后面會提到
Proxy:設(shè)置代理港粱,通常為類型(http、socks)和套接字地址。參考文章:直接使用Proxy創(chuàng)建連接
ProxySelector: 設(shè)置全局的代理查坪,通過繼承該類寸宏,設(shè)置具體代理的類型、地址和端口偿曙。參考文章:Java代理 通過ProxySelector設(shè)置全局代理
-
Protocol: 網(wǎng)絡(luò)協(xié)議類氮凝,比如我們經(jīng)常聽到的http1.0、http1.1望忆、http2.0協(xié)議等罩阵。
-
ConnectionSpec: 指定HTTP流量通過的套接字連接的配置。我們直接可以翻譯該類頭部的英文介紹启摄,具體的內(nèi)容原諒我也不是很懂:
Interceptor:攔截器稿壁,后面會提到
-
EventListener:指標(biāo)事件的監(jiān)聽器。擴(kuò)展此類以監(jiān)視應(yīng)用程序的HTTP調(diào)用的數(shù)量歉备,大小和持續(xù)時間傅是。所有start/connect/acquire事件最終都會收到匹配的end /release事件,要么成功(非null參數(shù))要么失斃傺颉(非null throwable)喧笔。每個事件對的第一個公共參數(shù)用于在并發(fā)或重復(fù)事件的情況下鏈接事件,例如dnsStart(call龟再,domainName);和dnsEnd(call书闸,domainName,inetAddressList); 我們可以看到一系列的xxxStart和xxxEnd方法:
-
CookieJar:向傳出的HTTP請求添加cookie,收到的HTTP返回數(shù)據(jù)的cookie處理吸申。
-
Cache:網(wǎng)絡(luò)緩存梗劫,okhttp默認(rèn)只能設(shè)置緩存GET請求,不緩存POST請求截碴,畢竟POST請求很多都是交互的,緩存下來也沒有什么意義蛉威。
我們看到Cache的構(gòu)造函數(shù)日丹,可以看到的是需要設(shè)置緩存文件夾,緩存的大小蚯嫌,還有一個是緩存內(nèi)部的操作方式哲虾,因為緩存是需要寫入文件的蜂嗽,默認(rèn)操作使用的是Okio來操作匿辩。
參考文章:</br>教你如何使用okhttp緩存</br>OKHTTP之緩存配置詳解 InternalCache:Okhttp內(nèi)部緩存的接口,我們直接使用的時候不需要去實現(xiàn)這個接口富稻,而是直接去使用上面的Cache類栅盲。
SocketFactory:從字面意思就看的出來汪诉,Android 自帶的Socket的工廠類。
參考文章: 類SocketFactorySSLSocketFactory:Android自帶的SSLSocket的工廠類。
參考文章:Java SSLSocket的使用 </br> 用SSLSocketFactory 連接https的地址-
CertificateChainCleaner:不是很了解扒寄,所以還是老樣子鱼鼓,通過谷歌翻譯,翻譯該類的頂部備注說明:
HostnameVerifier:字面意思该编,Host name 驗證迄本,這個一個基礎(chǔ)接口,而且只有一個方法:
/**
* Verify that the host name is an acceptable match with
* the server ‘s authentication scheme.
*
* @param hostname the host name
* @param session SSLSession used on the connection to host
* @return true if the host name is acceptable
*/
public boolean verify(String hostname, SSLSession session);
-
Dns:DNS(Domain Name System课竣,域名系統(tǒng))嘉赎,dns用于將域名解析解析為ip地址。
參考文章:Android DNS更新與DNS-Prefetch - 還有其他等等......
2.2 Request相關(guān)
我們查看Request代碼:
public final class Request {
final HttpUrl url; //網(wǎng)絡(luò)請求路徑
final String method; //get于樟、post.....
final Headers headers;//請求頭
final @Nullable RequestBody body;//請求體
/**
你可以通過tags來同時取消多個請求公条。
當(dāng)你構(gòu)建一請求時,使用RequestBuilder.tag(tag)來分配一個標(biāo)簽隔披。
之后你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個tag的call赃份。.
*/
final Map<Class<?>, Object> tags;
.......
.......
.......
}
這個估計很多人都清楚,如果對請求頭請求體等不清楚的奢米,可以看下以前我們這個系列的文章:Android技能樹 — 網(wǎng)絡(luò)小結(jié)(3)之HTTP/HTTPS
2.3 Call相關(guān)
我們可以看到我們生成的Request實例抓韩,會傳給OkHttpClient實例的new?all方法:
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.execute();或者 call.enqueue(....);
我們Request和OkHttpClient大致都了解過了,我們來具體看下newCall執(zhí)行了什么和Call的具體內(nèi)容鬓长。
Call類代碼:
@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的實例,同時把我們各種參數(shù)都配置好的OkHttpClient和Request都傳入了涉波。
所以后面call.execute()/call.enqueue()
都是執(zhí)行的RealCall的相對應(yīng)的方法英上。但目前位置我們上面的圖已經(jīng)講解好了,我這里再貼一次:
恭喜你啤覆,下次別人考你Okhttp前面的相關(guān)參數(shù)配置方面的代碼你已經(jīng)都理解了苍日。
3.請求分發(fā)Dispatcher
我們繼續(xù)看我們的流程圖下面的內(nèi)容:
3.1 Dispatcher 同步操作
我們先來講同步執(zhí)行:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//'1. 執(zhí)行了dispatcher的executed方法'
client.dispatcher().executed(this);
//'2. 調(diào)用了getResponseWithInterceptorChain方法'
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//'3. 最后一定會執(zhí)行dispatcher的finished方法'
client.dispatcher().finished(this);
}
}
我們一步步來具體看,第一步看Dispatcher類中的executed方法了:
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到把我們的RealCall加入到了一個同步線程runningSyncCalls
中窗声,然后中間調(diào)用了getResponseWithInterceptorChain
方法*(這個第二個操作我們會放在后面很具體的講解)相恃,我們既然加入到了一個同步線程中,肯定用完了要移除笨觅,然后第三步finished方法會做處理:
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//'if語句里面我們可以看到這里把我們的隊列中移除了call對象'
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();
}
}
3.2 Dispatcher 異步操作
我們先來看RealCall
里面的enqueue
代碼:
@Override public void enqueue(Callback responseCallback) {
//'1. 這里有個同步鎖的拋異常操作'
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//'2. 調(diào)用Dispatcher里面的enqueue方法'
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
我們一步步來看拦耐,第一個同步鎖拋異常的操作,我們知道一個Call應(yīng)對一個網(wǎng)絡(luò)請求见剩,加入你這么寫是錯誤的:
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {}
});
//'同一個call對象再次發(fā)起請求'
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {}
});
同一個Call對象杀糯,同時請求了二次。這時候就會進(jìn)入我們的同步鎖判斷苍苞,只要一個執(zhí)行過了固翰,里面 executed會為true
,也就會拋出異常。
我們再來看第二步操作:
我們知道異步請求倦挂,肯定會代表很多請求都在各自的線程中去執(zhí)行畸颅,那么我們在不看OkHttp源碼前,讓你去實現(xiàn)方援,你怎么實現(xiàn)没炒,是不是第一個反應(yīng)是使用線程池。
Java/Android線程池框架的結(jié)構(gòu)主要包括3個部分
1.任務(wù):包括被執(zhí)行任務(wù)需要實現(xiàn)的接口類:Runnable 或 Callable
2.任務(wù)的執(zhí)行器:包括任務(wù)執(zhí)行機(jī)制的核心接口類Executor犯戏,以及繼承自Executor的EexcutorService接口送火。
3.執(zhí)行器的創(chuàng)建者,工廠類Executors
具體可以參考:Android 線程池框架先匪、Executor种吸、ThreadPoolExecutor詳解
client.dispatcher().enqueue(new AsyncCall(responseCallback));
,不再是像同步操作一樣,直接把RealCall傳入呀非,而是傳入一個AsyncCall
對象坚俗。沒錯,按照我們上面提到的線程池架構(gòu)岸裙,任務(wù)是使用Runnable 或 Callable接口
猖败,我們查看AsyncCall的代碼:
final class AsyncCall extends NamedRunnable {
......
......
}
public abstract class NamedRunnable implements Runnable {
.......
.......
}
果然如我們預(yù)計,是使用了Runnable接口降允。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
,不再是像同步操作一樣恩闻,直接把RealCall傳入,而是傳入一個AsyncCall
對象剧董。
調(diào)用Dispatcher里面的enqueue方法:
synchronized void enqueue(AsyncCall call) {
//'1. 判斷當(dāng)前異步隊列里面的數(shù)量是否小于最大值幢尚,當(dāng)前請求數(shù)是否小于最大值'
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//'2. 如果沒有大于最大值,則將call加入到異步請求隊列中'
runningAsyncCalls.add(call);
//'3. 并且運行call的任務(wù)'
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
我么直接看第三步翅楼,按照我們上面提到過的Java/Android線程池框架的結(jié)構(gòu)主要包括3個部分尉剩,可以看到執(zhí)行我們的Runnable對象的,說明他是一個任務(wù)執(zhí)行器毅臊,也就是Executor的繼承類边涕。說明executorService()返回了一個Executor的實現(xiàn)類,我們點進(jìn)去查看:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
果然創(chuàng)建一個可緩存線程池褂微,線程池的最大長度無限制,但如果線程池長度超過處理需要园爷,可靈活回收空閑線程宠蚂,若無可回收,則新建線程童社。
那我們知道是線程池執(zhí)行了Runnable的任務(wù)求厕,那我們只要具體看我們的okhttp的Runnable到底執(zhí)行了什么即可:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
......
......
......
@Override protected void execute() {
boolean signalledCallback = false;
try {
//'1. 我們可以發(fā)現(xiàn)最后線程池執(zhí)行的任務(wù)就是getResponseWithInterceptorChain方法'
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//'2. 最后再從Dispatcher里面的異步隊列中移除'
client.dispatcher().finished(this);
}
}
}
我們發(fā)現(xiàn)不管是異步還是同步,都是一樣的三部曲:1.加入到Dispatcher里面的同步(或異步)隊列,2.執(zhí)行g(shù)etResponseWithInterceptorChain方法呀癣,3.從Dispatcher里面的同步(或異步)隊列移除美浦。(只不過同步操作是直接運行了getResponseWithInterceptorChain方法,而異步是通過線程池執(zhí)行Runnable再去執(zhí)行g(shù)etResponseWithInterceptorChain方法)
4 Okhttp攔截
我們在前面已經(jīng)知道了不管是異步請求還是同步請求项栏,都會去執(zhí)行
RealCall
的getResponseWithInterceptorChain
操作:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//'1. 創(chuàng)建一個攔截器List'
List<Interceptor> interceptors = new ArrayList<>();
//'2. 添加用戶自己創(chuàng)建的應(yīng)用攔截器'
interceptors.addAll(client.interceptors());
//'3. 添加重試與重定向攔截器'
interceptors.add(retryAndFollowUpInterceptor);
//'4. 添加內(nèi)容攔截器'
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//'4. 添加緩存攔截器'
interceptors.add(new CacheInterceptor(client.internalCache()));
/'5. 添加連接攔截器'
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//'6. 添加用戶自己創(chuàng)建的網(wǎng)絡(luò)攔截器'
interceptors.addAll(client.networkInterceptors());
}
//'7. 添加請求服務(wù)攔截器'
interceptors.add(new CallServerInterceptor(forWebSocket));
//'8.把這些攔截器們一起封裝在一個攔截器鏈條上面(RealInterceptorChain)'
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//'9.然后執(zhí)行鏈條的proceed方法'
return chain.proceed(originalRequest);
}
我們先不管具體的攔截器的功能浦辨,我們先來看整體的執(zhí)行方式,所以我們直接來看攔截器鏈條的工作模式:
public final class RealInterceptorChain implements Interceptor.Chain {
//'我們剛才建立的放攔截器的隊列'
private final List<Interceptor> interceptors;
//'當(dāng)前執(zhí)行的第幾個攔截器序號'
private final int index;
......
......
......
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
......
......
......
//'實例化了一個新的RealInterceptorChain對象沼沈,并且傳入相同的攔截器List流酬,只不過傳入的index值+1'
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//'獲取當(dāng)前index對應(yīng)的攔截器里面的具體的某個攔截器,
Interceptor interceptor = interceptors.get(index);
//然后執(zhí)行攔截器的intercept方法,同時傳入新的RealInterceptorChain對象(主要的區(qū)別在于index+1了)'
Response response = interceptor.intercept(next);
......
......
......
return response;
}
}
我們可以看到在RealInterceptorChain類的proceed的方法里面列另,又去實例化了一個RealInterceptorChain類芽腾。很多人可能看著比較繞,沒關(guān)系页衙,我們舉個例子簡單說下就可以了:
我的寫法還是按照它的寫法,寫了二個Interceptor,一個用來填充地址AddAddressInterceptor摊滔,一個中來填充電話AddTelephoneInterceptor,然后也建立一個攔截鏈條InterceptorChain店乐。這樣我只需要傳進(jìn)去一個字符串艰躺,然后會自動按照每個攔截器的功能,自動幫我填充了地址和電話號碼响巢。
Interceptor只負(fù)責(zé)處理自己的業(yè)務(wù)功能描滔,比如我們這里是填充地址和手機(jī)號碼,然后自己的任務(wù)結(jié)束就會調(diào)用攔截器鏈條踪古,執(zhí)行鏈條接下去的任務(wù)含长,其他跟Interceptor無關(guān)。
我們來看我們的攔截器和攔截器鏈條:
電話攔截器:
地址攔截器:
攔截器鏈:
Activity.java:
最后我們可以看到我們的result結(jié)果為:
這里額外提下: 里面的攔截器里面的二個大步驟是可以交換順序的伏穆,我先執(zhí)行攔截鏈的方法拘泞,讓它提前去執(zhí)行下一個攔截器的操作,再拿相應(yīng)的返回值做我這個攔截器的操作枕扫。比如還是剛才那個電話攔截器陪腌,我們調(diào)換了二個的順序:
這樣就會去先執(zhí)行地址攔截器,然后拿到結(jié)果后再去處理電話攔截器的邏輯烟瞧,所以最后的輸出結(jié)果為:
這里我們懂了以后诗鸭,我們再去看Okhttp前面提到的攔截器添加,攔截鏈的相關(guān)代碼参滴,是不是簡單的一比强岸,它的鏈接鏈的操作跟我們的基本架構(gòu)一致,然后各自的攔截器無非就是處理各自的邏輯砾赔,對參數(shù)進(jìn)行更改蝌箍,發(fā)起請求等青灼。所以我們的核心變成了OkHttp的各個攔截器到底做了什么邏輯。(也就是我們提到的攔截器中的二個大操作的其中一步妓盲,自己的處理邏輯杂拨。)
<font color = "red">本來想一步步的來寫每個單獨的攔截器的作用,后來想了下悯衬,單獨攔截器的代碼分析的文章真的太多太多了弹沽。而且每個攔截器寫的很簡單,其實沒啥大的意義甚亭,寫的仔細(xì)贷币,一個攔截器就可以是一篇文章,而我們本文也側(cè)重于總體的源碼架構(gòu)亏狰,所以我后面如果可以的役纹,都直接引用別人的文章了。</font>
4.1 RetryAndFollowUpInterceptor
看名字就知道這個攔截器的作用是重試和重定向的暇唾。
大家可以參考本文:
OKhttp源碼解析---攔截器之RetryAndFollowUpInterceptor
4.2 BridgeInterceptor
我們來看BridgeInterceptor
類的說明備注:
什么促脉?看不懂英文,谷歌翻譯走起:
簡單來說策州,我們自己在Okhttp里面建立了一個Request請求對象瘸味,但是這個對象并不是直接就可以用來馬上發(fā)送網(wǎng)絡(luò)請求的,畢竟我們剛開始實例化Request的時候就簡單的放入了Url够挂,body等旁仿,很多參數(shù)都是沒有設(shè)置的,所以我們還需要補(bǔ)充很多參數(shù)孽糖,然后發(fā)起網(wǎng)絡(luò)請求枯冈,然后網(wǎng)絡(luò)返回的參數(shù),我們再把它封裝成Okhttp可以直接使用的對象办悟。
一句話概括: 將客戶端構(gòu)建的Request對象信息構(gòu)建成真正的網(wǎng)絡(luò)請求;然后發(fā)起網(wǎng)絡(luò)請求尘奏,最后就是將服務(wù)器返回的消息封裝成一個Response對象
參考文章:
4.3 CacheInterceptor
緩存攔截器,簡單來說就是有緩存就使用緩存病蛉。
參考文章:
4.4 ConnectInterceptor
連接攔截器炫加,顧名思義打開了與服務(wù)器的鏈接,正式開啟了網(wǎng)絡(luò)請求铺然。
因為以前在文章:
Android技能樹 — 網(wǎng)絡(luò)小結(jié)(4)之socket/websocket/webservice
提到過俗孝,我們的請求是通過Socket去訪問的。
所以最終這個ConnectInterceptor也會去發(fā)起一個Socket連接請求魄健。
參考文章:
4.5 CallServerInterceptor
我們曾經(jīng)在文章 Android技能樹 — 網(wǎng)絡(luò)小結(jié)(2)之TCP/UDP 提過:
TCP要先建立通道驹针,然后再發(fā)送數(shù)據(jù)柬甥。
上面的攔截器ConnectInterceptor已經(jīng)幫我們把通道建立好了其垄,所以在這個CallServerInterceptor攔截器里面,我們的任務(wù)就是發(fā)送相關(guān)的數(shù)據(jù)臂外,
參考文章:
Okhttp之CallServerInterceptor簡單分析
4.6 自定義攔截器
我們在流程圖中看到了喇颁,除了OKHttp源碼里面自帶的攔截器,還有二種自定義攔截器橘霎,應(yīng)用攔截器和網(wǎng)絡(luò)攔截器。
使用代碼:
okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(appInterceptor)//Application攔截器
.addNetworkInterceptor(networkInterceptor)//Network攔截器
.build();
我們知道網(wǎng)絡(luò)請求中間一定要經(jīng)過一系列的攔截器姐叁,我們也可以自己寫攔截器瓦盛,然后對里面的參數(shù)做處理,比如我們對Request在攔截器中做某個寫參數(shù)變更外潜,然后再交給下一個攔截器原环。
而這二個自定義攔截器的位置,在我們前面分析獲取攔截鏈的方法getResponseWithInterceptorChain中就提過了处窥,現(xiàn)在再拿出來重新說一遍:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
'最先添加用戶的自定義APPlication攔截器'
interceptors.addAll(client.interceptors());
'然后是一系列的Okhttp自帶的攔截器'
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
'在最終跟服務(wù)器交互數(shù)據(jù)的CallServerInterceptor前嘱吗,添加用戶自定義的NetWork攔截器'
'因為如果放在最后就沒什么意義了。'
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);
}
參考文章:
結(jié)語:
OkHttp源碼寫的也比較倉促滔驾,特別后面的各個攔截器的源碼分析就偷懶了谒麦,因為不然會引出一大段一大段的內(nèi)容,就直接引用其他大佬的文章嵌灰。如果哪里不對弄匕,歡迎大家指出。