- 本文章所使用的 OkHttp 源碼版本:3.12.10
上一篇:OkHttp 精講:攔截器執(zhí)行原理
源碼解析
- 那么問題來(lái)了,一個(gè)攔截器的源碼那么多借宵,我們?cè)搹暮稳胧帜兀?/li>
- 我們可以看到攔截器所實(shí)現(xiàn)的一個(gè) Interceptor 接口辐赞,里面只有一個(gè) intercept 方法,那么我們就從這個(gè)方法開始入手
- 一眼望去河闰,最吸引我們眼球的,莫屬這個(gè) while 循環(huán)褥紫,因?yàn)槲覀冎爸v過(guò)姜性,攔截器執(zhí)行原理就是使用了遞歸,現(xiàn)在怎么還在遞歸里面使用循環(huán)了呢髓考?到底是在做什么騷操作部念?
- 在這里我們可以看到,當(dāng)前攔截器將請(qǐng)求通過(guò)攔截鏈傳遞給下一個(gè)攔截器
- 有意思的地方是氨菇,在這個(gè)傳遞過(guò)程中儡炼,有兩個(gè) catch 塊,這兩個(gè) catch 塊的結(jié)果都是要么拋出異常查蓉,要么繼續(xù)循環(huán)
- 那么什么情況下會(huì)拋異常呢乌询?我們可以通過(guò)源碼看出,它是通過(guò)調(diào)用 recover 方法來(lái)決定是否拋出異常豌研,接下來(lái)讓我們看看這個(gè)方法是干什么用的
看完這個(gè)方法妹田,我相信大多數(shù)人的反應(yīng)跟我第一次看的時(shí)候一樣唬党,還是沒看懂這個(gè)方法有什么用
看不懂源碼不要緊,可以看源碼上面的注釋鬼佣,但注釋是英文的也是看不懂該怎么辦驶拱?可以翻譯成中文再看。有一句老話說(shuō)得好晶衷,只要思想不滑坡蓝纲,方法總比困難多。阻擋我們前進(jìn)的不是困難本身晌纫,而是我們內(nèi)心的恐懼税迷,而唯一的方法就是戰(zhàn)勝它
通過(guò)這些注釋,我們可以得出缸匪,這個(gè)方法用于判斷請(qǐng)求是否還能被恢復(fù)翁狐,如果可以的話就進(jìn)行重新請(qǐng)求,如果不行就直接拋出異常給上層
這個(gè)便是 OkHttp 重試機(jī)制凌蔬,如果錯(cuò)誤不是致命的露懒,就對(duì)請(qǐng)求進(jìn)行重試,正如這個(gè)攔截器的名字一樣砂心, RetryAndFollowUpInterceptor懈词,Retry 是重試的意思,And 是和的意思辩诞,那么 FollowUp 是什么意思坎弯?
- 光靠這個(gè)單詞的意思我們并不能得出答案,接下來(lái)我們將通過(guò)剩余的源碼來(lái)揭曉答案
- 這個(gè)方法看著很可疑译暂,看名字跟我們想知道的 followUp 作用有很大關(guān)系抠忘,讓我們先進(jìn)去這個(gè)方法里面看看
- 看完的第一個(gè)感覺還是懵,不過(guò)還是用老方法來(lái)解決這個(gè)問題
處理客戶端超時(shí)大家都懂外永,這里不再做過(guò)多解釋崎脉,但是重定向伯顶,這個(gè)專業(yè)術(shù)語(yǔ)可能對(duì)于做 App 開發(fā)的人來(lái)說(shuō)還是很陌生囚灼,在這里我給大家舉個(gè)栗子灶体,大家可以打開這個(gè)鏈接:http://www.baidu.com锥涕,然后再仔細(xì)瞅瞅網(wǎng)址變了沒刁笙?
沒錯(cuò),我們明明輸入的是 http://www.baidu.com捂寿,但是卻跳轉(zhuǎn)到 https://www.baidu.com 上了
如果你聽不懂我想表達(dá)什么更卒,那么說(shuō)明這個(gè)栗子還是不夠生動(dòng)形象,我再舉一個(gè)栗子
大家可以打開這個(gè)鏈接:https://www.xiaomi.com斤彼,是不是就變成了 https://www.mi.com 土陪?
這種功能就叫做請(qǐng)求重定向昼汗,屬于 Http 協(xié)議的內(nèi)容
如果你看完了整個(gè) OkHttp 的源碼,其實(shí)不難發(fā)現(xiàn)鬼雀,如果對(duì) Http 協(xié)議不夠了解顷窒,很多源碼基本沒辦法看懂
那么我們又該如何了解 Http 協(xié)議呢?
讓我們回顧一下 Http 的組成部分源哩,Http 報(bào)文大致分為兩種:請(qǐng)求報(bào)文和響應(yīng)報(bào)文鞋吉,分別對(duì)應(yīng)著 OkHttp 中的 Request 和 Response 類,其中請(qǐng)求報(bào)文內(nèi)容大致分為請(qǐng)求頭璧疗、Body(文件流)坯辩,響應(yīng)報(bào)文內(nèi)容大致分為響應(yīng)頭、響應(yīng)碼崩侠、Body(文件流)
-
請(qǐng)求的 Body 就是我們傳入的表單參數(shù)或者文件流漆魔,而響應(yīng)的 Body 就是后臺(tái)返回給我們的文本或者文件流,這塊屬于業(yè)務(wù)却音,和 Http 協(xié)議并無(wú)太大關(guān)系改抡,在這里我們先簡(jiǎn)單了解一下請(qǐng)求頭、響應(yīng)頭系瓢、響應(yīng)碼阿纤,在這里我們可以參考以下這兩個(gè)對(duì)照表
-
關(guān)于請(qǐng)求重定向這塊的源碼,必須要對(duì)響應(yīng)碼的有一定的了解夷陋,所以我們先簡(jiǎn)單回顧一下響應(yīng)碼的區(qū)間和類型
100-199:請(qǐng)求處理中
200-299:請(qǐng)求被接受(例如:200 請(qǐng)求成功)
300-399:請(qǐng)求被重定向(例如:307 地址重定向)
400-499:客戶端請(qǐng)求參數(shù)錯(cuò)誤(例如:403 請(qǐng)求被拒絕欠拾,404 請(qǐng)求地址不存在)
500-599:服務(wù)器解析請(qǐng)求發(fā)生錯(cuò)誤(例如:500 服務(wù)器出錯(cuò),503 接口出錯(cuò))
我們可以得出骗绕,請(qǐng)求重定向的響應(yīng)碼區(qū)間在 300-399藐窄,那么我們只看這個(gè)區(qū)間響應(yīng)碼的源碼
- 這段代碼是什么意思,我相信大部分人都不能理解酬土,這塊的響應(yīng)碼是 307 和 308荆忍,那么讓我們回顧一下剛剛看過(guò)的響應(yīng)碼表,看看這個(gè) 307 到底是什么意思?
狀態(tài)碼 | 含義 |
---|---|
307 | 請(qǐng)求的資源現(xiàn)在臨時(shí)從不同的 URI 響應(yīng)請(qǐng)求刹枉。由于這樣的重定向是臨時(shí)的叽唱,客戶端應(yīng)當(dāng)繼續(xù)向原有地址發(fā)送以后的請(qǐng)求。只有在 Cache-Control 或 Expires 中進(jìn)行了指定的情況下微宝,這個(gè)響應(yīng)才是可緩存的棺亭。新的臨時(shí)性的URI 應(yīng)當(dāng)在響應(yīng)的 Location 域中返回。除非這是一個(gè) HEAD 請(qǐng)求芥吟,否則響應(yīng)的實(shí)體中應(yīng)當(dāng)包含指向新的URI 的超鏈接及簡(jiǎn)短說(shuō)明侦铜。因?yàn)椴糠譃g覽器不能識(shí)別307響應(yīng),因此需要添加上述必要信息以便用戶能夠理解并向新的 URI 發(fā)出訪問請(qǐng)求钟鸵。如果這不是一個(gè) GET 或者 HEAD 請(qǐng)求,那么瀏覽器禁止自動(dòng)進(jìn)行重定向涤躲,除非得到用戶的確認(rèn)棺耍,因?yàn)檎?qǐng)求的條件可能因此發(fā)生變化。 |
敲黑板种樱,劃重點(diǎn):如果這不是一個(gè) GET 或者 HEAD 請(qǐng)求蒙袍,那么瀏覽器禁止自動(dòng)進(jìn)行重定向
意思很明顯了,如果服務(wù)器返回 307 或 308嫩挤,那么如果當(dāng)前的請(qǐng)求方式不是 GET 或 HEAD害幅,那么本次的重定向請(qǐng)求就會(huì)被客戶端拒絕
在這里我們默認(rèn)它就是符合要求的,然后我們接著往下面看
- 這里解釋一下 switch 語(yǔ)法岂昭,如果 case 塊中沒有 break 或 return以现,那么它就會(huì)往下一個(gè) case 塊執(zhí)行,直到下一個(gè) case 塊中有 break 或 return 才會(huì)跳出整個(gè) switch约啊。
- 會(huì)先判斷我們?cè)?OkHttpClient 有沒有禁止重定向邑遏,有的話也會(huì)直接拒絕本次的重定向請(qǐng)求
- 這個(gè)是啥?我相信大多數(shù)人看完都愣了一下恰矩,那么這個(gè) Location 的響應(yīng)頭到底是什么呢记盒?接下來(lái)讓我們回顧一下剛剛看過(guò)的請(qǐng)求頭和響應(yīng)頭對(duì)照表
名稱 | 說(shuō)明 | 示例 |
---|---|---|
Location | 用來(lái)重定向接收方到非請(qǐng)求URL的位置來(lái)完成請(qǐng)求或標(biāo)識(shí)新的資源 | Location: http://www.reibang.com/u/f7bb67d86765 |
如果說(shuō)你還是不能理解,那只能建議你打開:http://www.baidu.com
目的不是讓你百度一下外傅,而是先在一個(gè)空網(wǎng)頁(yè)上打開瀏覽器的開發(fā)者選項(xiàng)纪吮,然后再打開這個(gè)網(wǎng)址
- 看到這里,我相信大家都應(yīng)該明白了萎胰,既然是重定向碾盟,唯一不能少的就是重定向的地址
- 重新對(duì)請(qǐng)求頭進(jìn)行封裝,然后返回給攔截器
源碼總結(jié)
RetryAndFollowUpInterceptor 主要作用有兩個(gè):重試和重定向
重試則是 OkHttp 自身的機(jī)制奥洼,如果請(qǐng)求失敗了巷疼,那么會(huì)在這上面會(huì)判斷這個(gè)錯(cuò)誤是不是致命,如果不是的話就會(huì)對(duì)請(qǐng)求進(jìn)行重試,否則就會(huì)拋出異常給上層嚼沿。
重定向則是 Http 協(xié)議的一部分內(nèi)容估盘,OkHttp 選擇將這一部分的邏輯放置到這個(gè)攔截器中,當(dāng)服務(wù)器返回 3 開頭的響應(yīng)碼時(shí)骡尽,會(huì)先判斷當(dāng)前是否禁用了請(qǐng)求重定向遣妥,有的話就不進(jìn)行重定向,如果沒有的話就會(huì)從響應(yīng)頭中取 Location 字段的值作為重定向地址攀细,然后將這個(gè)地址重新封裝到 Request 對(duì)象中箫踩,最后將新的 Request 對(duì)象返回給上層進(jìn)行重新請(qǐng)求。
無(wú)論是重試還是重定向谭贪,它們都有一個(gè)共同的特點(diǎn)境钟,都可以進(jìn)行重新請(qǐng)求,所以它們才會(huì)被放在同一個(gè)攔截器中俭识。