知識(shí)點(diǎn)匯總:
一:Retrofit項(xiàng)目介紹與實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求原理
二:Okhttp的項(xiàng)目介紹與實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的原理
三:項(xiàng)目中使用到的設(shè)計(jì)模式場(chǎng)景分析
四:項(xiàng)目中的核心類分析
五:常見問題匯總
六:擴(kuò)展閱讀
一:Retrofit項(xiàng)目介紹與實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求原理
官方簡(jiǎn)介:Retrofit是您的API接口轉(zhuǎn)換為可調(diào)用對(duì)象的類,默認(rèn)情況下祝谚,Retrofit將為您的平臺(tái)提供合理的默認(rèn)設(shè)置,但它也允許自定義。
項(xiàng)目結(jié)構(gòu)圖:
retrofit:項(xiàng)目的具體實(shí)現(xiàn)源碼和自定義的網(wǎng)絡(luò)請(qǐng)求相關(guān)注解。
retrofit-adapters:Retrofit附帶一個(gè)用于執(zhí)行調(diào)用實(shí)例的默認(rèn)適配器,這里包含的子模塊是其他流行執(zhí)行機(jī)制的附加適配器,要使用請(qǐng)?jiān)跇?gòu)建改造實(shí)例時(shí)提供所需適配器的實(shí)例空盼。
retrofit-converters:Retrofit附帶了對(duì)OkHttp的RequestBody和ResponseBody類型的支持衰抑,但該庫與內(nèi)容格式無關(guān)饼记,這里包含的子模塊是其他流行格式的附加轉(zhuǎn)換器香伴,要使用請(qǐng)?jiān)跇?gòu)建改造實(shí)例時(shí)提供所需轉(zhuǎn)換器的實(shí)例。
samples:項(xiàng)目的接口調(diào)用示例代碼具则。(包含12個(gè)示例代碼)
Retrofit實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求解析:
第一步:通過建造者模式創(chuàng)建復(fù)雜的對(duì)象Retrofit類的示例即纲,這里的build函數(shù)通過建造者模式中鏈?zhǔn)秸{(diào)用把相關(guān)傳入的參數(shù)來初始化Retrofit的內(nèi)部變量,如果鏈?zhǔn)秸{(diào)用中沒有設(shè)置相關(guān)參數(shù)博肋,build函數(shù)里面也會(huì)設(shè)置相關(guān)的默認(rèn)實(shí)現(xiàn)的參數(shù)低斋,執(zhí)行默認(rèn)操作實(shí)現(xiàn)。
下面我們通過一個(gè)官方提供的Demo示例束昵,查看如何通過Retrofit實(shí)現(xiàn)一次簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求拔稳,代碼如下:
自定義網(wǎng)絡(luò)請(qǐng)求接口類:
Retrofit類成員變量:
函數(shù)解析:
1、Map<Method, ServiceMethod<?>> serviceMethodCache:這是一個(gè)方法的緩存類锹雏,key為網(wǎng)絡(luò)請(qǐng)求的Method巴比,比如GET,POST等礁遵,而ServiceMethod則對(duì)應(yīng)著動(dòng)態(tài)代理解析后的方法類轻绞。
2、okhttp3.Call.Factory callFactory:這個(gè)是創(chuàng)建OkHttp的工廠類佣耐。
3政勃、HttpUrl baseUrl:這個(gè)是基礎(chǔ)URL,網(wǎng)絡(luò)請(qǐng)求會(huì)帶上這個(gè)基礎(chǔ)URL兼砖。
4奸远、List<Converter.Factory> converterFactories:Converter.Factory的集合,Converter.Factory是將返回的數(shù)據(jù)通過這個(gè)工廠轉(zhuǎn)化為對(duì)應(yīng)的數(shù)據(jù)讽挟,比如Gson的GsonConverterFactory工廠類懒叛,也就是數(shù)據(jù)轉(zhuǎn)化器工廠。
5耽梅、List<CallAdapter.Factory> callAdapterFactories:CallAdapter.Factory的集合薛窥,CallAdapter.Factory是網(wǎng)絡(luò)請(qǐng)求的適配器工廠,比如把Call轉(zhuǎn)化為RxJava請(qǐng)求的RxJavaCallAdapterFactory工廠眼姐,也就是Call轉(zhuǎn)化工廠诅迷。
6、Executor callbackExecutor:用于回調(diào)網(wǎng)絡(luò)請(qǐng)求众旗。
7罢杉、boolean validateEagerly:用于判斷是否需要立即解析方法。
build函數(shù)實(shí)現(xiàn):(刪除部分代碼)
? ? ? ?這里設(shè)置建造者模式鏈?zhǔn)秸{(diào)用傳入的相關(guān)參數(shù)贡歧,如果沒有傳入滩租,則設(shè)置默認(rèn)實(shí)現(xiàn)參數(shù)拱镐,其中還包含部分額外的初始操作,執(zhí)行完該函數(shù)持际,Retrofit類對(duì)象就創(chuàng)建成功了。
第二步:自定義接口類Github通過接口傳入create()函數(shù)中哗咆,在create函數(shù)中首先會(huì)檢查自定義接口函數(shù)是否符合要求(不支持泛型蜘欲、靜態(tài)函數(shù)等),不符合則拋出異常晌柬,接著姥份,會(huì)通過Proxy類創(chuàng)建代理類,在代理類的invoke函數(shù)根據(jù)不同的平臺(tái)(Android年碘、java等)把接口類中定義的請(qǐng)求函數(shù)(Mathod)澈歉,轉(zhuǎn)換為具體的請(qǐng)求對(duì)象HttpServiceMethod,并緩存到ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu)中屿衅。
下面看看校驗(yàn)服務(wù)接口的實(shí)現(xiàn)代碼:(不符合條件的接口類就拋出異常)
接著我們看看自定義的接口中的請(qǐng)求函數(shù)埃难,是如何生成一個(gè)一個(gè)的網(wǎng)絡(luò)請(qǐng)求對(duì)象的(HttpServiceMethod),代碼如下:
? ?
第三步:最后通過github.contributors("square", "retrofit")和call.execute()函數(shù)執(zhí)行網(wǎng)絡(luò)請(qǐng)求涤久,實(shí)際上會(huì)觸發(fā)上面動(dòng)態(tài)代理類中的invoke函數(shù)執(zhí)行涡尘,最終通過反射類Method的invoke函數(shù),觸發(fā)HttpServiceMethod的invoke函數(shù)執(zhí)行响迂,而通過查看invoke的實(shí)現(xiàn)考抄,最終通過execute()函數(shù)執(zhí)行的網(wǎng)絡(luò)請(qǐng)求動(dòng)作是通過Okhttp實(shí)現(xiàn)的,所以說Retrofit只是對(duì)Okhttp進(jìn)行封裝實(shí)現(xiàn)蔗彤。
OkHttpCall類的execute()函數(shù)實(shí)現(xiàn)川梅,通過okhttp實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求:
??
Retrofit網(wǎng)絡(luò)請(qǐng)求流程圖:(包含部分Okhttp實(shí)現(xiàn))
二:Okhttp的項(xiàng)目介紹與實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的原理
官方簡(jiǎn)介:OkHttp是一個(gè)默認(rèn)高效的HTTP客戶端
1、HTTP2支持允許對(duì)同一主機(jī)的所有請(qǐng)求共享一個(gè)套接字然遏。
2贫途、連接池減少了請(qǐng)求延遲。
3啦鸣、透明GZIP縮小了下載大小潮饱。
4、響應(yīng)緩存完全避免了網(wǎng)絡(luò)重復(fù)請(qǐng)求诫给。
5香拉、請(qǐng)求失敗自動(dòng)重試主機(jī)的其他IP,自動(dòng)重定向:當(dāng)網(wǎng)絡(luò)出現(xiàn)問題時(shí)中狂,OkHttp的不斷重試凫碌,它會(huì)默默地從常見的連接問題中恢復(fù),如果您的服務(wù)有多個(gè)IP地址胃榕,如果第一次連接失敗盛险,OkHttp將嘗試備用地址瞄摊,這對(duì)于IPv4+IPv6和冗余數(shù)據(jù)中心中托管的服務(wù)是必要的,OkHttp支持現(xiàn)代TLS功能(TLS1.3苦掘、ALPN换帜、證書固定),它可以配置為回退以實(shí)現(xiàn)廣泛的連接鹤啡。
6惯驼、好用的API:使用OkHttp很容易,它的請(qǐng)求響應(yīng)API設(shè)計(jì)有流暢的構(gòu)建器和不變性递瑰,它支持同步阻塞調(diào)用和帶有回調(diào)的異步調(diào)用祟牲。
下面對(duì)于okhttp實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的操作,就不用Okhttp的示例作為分析抖部,還是接著Retrofit實(shí)現(xiàn)代碼接著講说贝,但是有興趣的讀者,本人下面也放上通過Okhttp做網(wǎng)絡(luò)請(qǐng)求的示例代碼慎颗。
Okhttp網(wǎng)絡(luò)請(qǐng)求解析:通過對(duì)上面的Retrofit的網(wǎng)絡(luò)請(qǐng)求邏輯分析乡恕,最后執(zhí)行到OkHttpCall類的execute()函數(shù),通過okhttp對(duì)網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)操作俯萎,返回?cái)?shù)據(jù)parseResponse(call.execute())几颜,下面我們就具體看看Okhttp是如何實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)請(qǐng)求操作的,并對(duì)返回?cái)?shù)據(jù)的相關(guān)處理讯屈。
? ?
第一步:在Okhttp內(nèi)部會(huì)通過Dispatcher類(分發(fā)器)實(shí)現(xiàn)對(duì)同步蛋哭、異步的網(wǎng)絡(luò)請(qǐng)求進(jìn)行存儲(chǔ)和網(wǎng)絡(luò)請(qǐng)求調(diào)配的,分發(fā)器中包含線程池的對(duì)象涮母、請(qǐng)求緩存隊(duì)列(雙端隊(duì)列)等內(nèi)部變量谆趾。
RealCall的execute()函數(shù)實(shí)現(xiàn):
? ? ?
第二步:在通過Dispatcher(分發(fā)器)調(diào)配相關(guān)網(wǎng)絡(luò)請(qǐng)求后,會(huì)通過系統(tǒng)設(shè)置的多個(gè)責(zé)任鏈攔截器實(shí)現(xiàn)最終的網(wǎng)絡(luò)請(qǐng)求動(dòng)作的執(zhí)行操作叛本,各種攔截器包括:
1沪蓬、RetryAndFollowUpInterceptor(負(fù)責(zé)重定向)
2、BridgeInterceptor(負(fù)責(zé)把用戶構(gòu)造的請(qǐng)求轉(zhuǎn)換為發(fā)送給服務(wù)器的請(qǐng)求来候,把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為對(duì)用戶友好的響應(yīng))
3跷叉、CacheInterceptor(負(fù)責(zé)讀取緩存以及更新緩存)
4、ConnectInterceptor(負(fù)責(zé)與服務(wù)器建立連接)
5营搅、CallServerInterceptor(負(fù)責(zé)從服務(wù)器讀取響應(yīng)的數(shù)據(jù))
? ? ? ? ?通過依次各個(gè)攔截器的邏輯處理云挟,最終在CallServerInterceptor攔截器中實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)數(shù)據(jù)的請(qǐng)求。
看看上面執(zhí)行到RealCall的getResponseWithInterceptorChain()函數(shù)實(shí)現(xiàn):
? ? ?
第三步:最后通過CallServerInterceptor攔截器實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求转质,并返回請(qǐng)求對(duì)象Response园欣,通過解析返回對(duì)象的數(shù)據(jù),最終實(shí)現(xiàn)一次完整的網(wǎng)絡(luò)請(qǐng)求行為休蟹。
? ? ?
Okhttp請(qǐng)求流程圖:
三:項(xiàng)目中使用到的設(shè)計(jì)模式場(chǎng)景分析
3.1沸枯、建造者模式
3.2日矫、代理模式(動(dòng)態(tài)代理)
3.3、責(zé)任鏈模式
3.4绑榴、工廠方法模式
3.5哪轿、享元模式
3.6、外觀模式
3.7翔怎、策略模式
建造者模式(Bulider模式):
定義:指將一個(gè)復(fù)雜對(duì)象的構(gòu)造與它的表示分離缔逛,使同樣的構(gòu)建過程可以創(chuàng)建不同的表示,這樣的設(shè)計(jì)模式被稱為建造者模式姓惑,它是將一個(gè)復(fù)雜的對(duì)象分解為多個(gè)簡(jiǎn)單的對(duì)象,然后一步一步構(gòu)建而成按脚。它將變與不變相分離于毙,即產(chǎn)品的組成部分是不變的,但每一部分是可以靈活選擇的辅搬。
優(yōu)點(diǎn):
1唯沮、封裝性好,構(gòu)建和表示分離堪遂。
2介蛉、擴(kuò)展性好,各個(gè)具體的建造者相互獨(dú)立溶褪,有利于系統(tǒng)的解耦币旧。
3、客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)猿妈,建造者可以對(duì)創(chuàng)建過程逐步細(xì)化吹菱,而不對(duì)其它模塊產(chǎn)生任何影響,便于控制細(xì)節(jié)風(fēng)險(xiǎn)彭则。
使用場(chǎng)景:
當(dāng)一個(gè)類的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)超過四個(gè)鳍刷,而這些參數(shù)有些是可選參數(shù),考慮使用建造者模式俯抖,OkHttpClient比較復(fù)雜输瓜,太多屬性,而且客戶的組合需求多樣化芬萍,所以O(shè)Khttp使用建造者模式尤揣,Retrofit類、Request類對(duì)象的創(chuàng)建也是通過建造者模式創(chuàng)建柬祠。
責(zé)任鏈模式:
定義:為了避免請(qǐng)求發(fā)送者與多個(gè)請(qǐng)求處理者耦合在一起芹缔,于是將所有請(qǐng)求的處理者通過前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈;當(dāng)有請(qǐng)求發(fā)生時(shí)瓶盛,可將請(qǐng)求沿著這條鏈傳遞最欠,直到有對(duì)象處理它為止示罗。
優(yōu)點(diǎn):
1、降低了對(duì)象之間的耦合度:該模式使得一個(gè)對(duì)象無須知道到底是哪一個(gè)對(duì)象處理其請(qǐng)求以及鏈的結(jié)構(gòu)芝硬,發(fā)送者和接收者也無須擁有對(duì)方的明確信息蚜点。
2、增強(qiáng)了系統(tǒng)的可擴(kuò)展性:可以根據(jù)需要增加新的請(qǐng)求處理類拌阴,滿足開閉原則绍绘。
3、增強(qiáng)了給對(duì)象指派職責(zé)的靈活性:當(dāng)工作流程發(fā)生變化迟赃,可以動(dòng)態(tài)地改變鏈內(nèi)的成員或者調(diào)動(dòng)它們的次序陪拘,也可動(dòng)態(tài)地新增或者刪除責(zé)任。
4纤壁、責(zé)任鏈簡(jiǎn)化了對(duì)象之間的連接:每個(gè)對(duì)象只需保持一個(gè)指向其后繼者的引用左刽,不需保持其他所有處理者的引用,這避免了使用眾多的if或者if else語句酌媒。
5欠痴、責(zé)任分擔(dān):每個(gè)類只需要處理自己該處理的工作,不該處理的傳遞給下一個(gè)對(duì)象完成秒咨,明確各類的責(zé)任范圍喇辽,符合類的單一職責(zé)原則。
使用場(chǎng)景:在okhttp中的攔截器模塊雨席,執(zhí)行過程用到菩咨,OkHttp3的攔截器鏈中,內(nèi)置了5個(gè)默認(rèn)的攔截器陡厘,分別用于重試旦委、請(qǐng)求對(duì)象轉(zhuǎn)換、緩存雏亚、鏈接缨硝、網(wǎng)絡(luò)讀寫。
OkHttp3責(zé)任鏈模式圖解:
? ? ? ?
代理模式(動(dòng)態(tài)代理):
定義:代理模式又叫委托模式罢低,是為某個(gè)對(duì)象提供一個(gè)代理對(duì)象查辩,并且由代理對(duì)象控制對(duì)原對(duì)象的訪問。代理模式通俗來講就是我們生活中常見的中介网持,代理模式可以提供非常好的訪問控制宜岛,應(yīng)用比較廣泛。
優(yōu)點(diǎn):
1功舀、職責(zé)清晰:具體角色是實(shí)現(xiàn)具體的業(yè)務(wù)邏輯萍倡,不用關(guān)心其他非本職責(zé)的事務(wù),通過后期的代理完成一件事務(wù)辟汰,代碼清晰列敲,在某些情況下阱佛,一個(gè)客戶類不想或者不能直接引用一個(gè)委托對(duì)象,而代理類對(duì)象可以在客戶類和委托對(duì)象之間起到中介的作用戴而,其特征是代理類和委托類實(shí)現(xiàn)相同的接口凑术。
2、高擴(kuò)展性:具體主題角色隨時(shí)會(huì)發(fā)生變化所意,但是只要實(shí)現(xiàn)了接口淮逊,接口不變,代理類就可以不做任何修改繼續(xù)使用扶踊,符合“開閉原則”泄鹏,另外,代理類除了是客戶類和委托類的中介之外秧耗,我們還可以通過給代理類增加額外的功能來擴(kuò)展委托類的功能备籽,這樣做我們只需要修改代理類而不需要再修改委托類,同樣符合開閉原則绣版。
代理模式有多種不同的實(shí)現(xiàn)方式,如果按照代理創(chuàng)建的時(shí)期來進(jìn)行分類:靜態(tài)代理歼疮、動(dòng)態(tài)代理杂抽。
1、靜態(tài)代理:由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼韩脏,再對(duì)其進(jìn)行編譯缩麸。在程序運(yùn)行之前,代理類.class文件就已經(jīng)被創(chuàng)建赡矢,代理類和委托類的關(guān)系在運(yùn)行前就確定杭朱。
2、動(dòng)態(tài)代理:動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成吹散,所以不存在代理類的字節(jié)碼文件弧械,代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定。
使用場(chǎng)景:在Retrofit的create函數(shù)執(zhí)行時(shí)空民,通過動(dòng)態(tài)代理模式刃唐,實(shí)現(xiàn)了網(wǎng)絡(luò)請(qǐng)求時(shí),通過代理類去做具體的操作實(shí)現(xiàn)(Method.invoke)界轩,最后實(shí)際是通過Okhttp實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的画饥。
工廠方法模式:
定義:這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式浊猾,在工廠模式中抖甘,我們?cè)趧?chuàng)建對(duì)象時(shí)不會(huì)對(duì)客戶端暴露創(chuàng)建邏輯,并且是通過使用一個(gè)共同的接口來指向新創(chuàng)建的對(duì)象葫慎。
優(yōu)點(diǎn):
1衔彻、用戶只需要知道具體工廠的名稱就可得到所要的產(chǎn)品薇宠,無須知道產(chǎn)品的具體創(chuàng)建過程。
2米奸、靈活性增強(qiáng)昼接,對(duì)于新產(chǎn)品的創(chuàng)建,只需多寫一個(gè)相應(yīng)的工廠類悴晰。
3慢睡、典型的解耦框架,高層模塊只需要知道產(chǎn)品的抽象類铡溪,無須關(guān)心其他實(shí)現(xiàn)類漂辐,滿足迪米特法則、依賴倒置原則和里氏替換原則棕硫。
缺點(diǎn):
1髓涯、類的個(gè)數(shù)容易過多,增加復(fù)雜度哈扮。
2纬纪、增加了系統(tǒng)的抽象性和理解難度。
3滑肉、抽象產(chǎn)品只能生產(chǎn)一種產(chǎn)品包各,此弊端可使用抽象工廠模式解決。
使用場(chǎng)景:Call接口提供了內(nèi)部接口Factory靶庙,用于將對(duì)象的創(chuàng)建延遲到該工廠類的子類中進(jìn)行问畅,從而實(shí)現(xiàn)動(dòng)態(tài)的配置,工廠方法模式六荒。
策略模式:
定義:一個(gè)類的行為或其算法可以在運(yùn)行時(shí)更改护姆,這種類型的設(shè)計(jì)模式屬于行為型模式,策略模式中掏击,我們創(chuàng)建表示各種策略的對(duì)象和一個(gè)行為隨著策略對(duì)象改變而改變的context對(duì)象卵皂,策略對(duì)象改變context對(duì)象的執(zhí)行算法。
優(yōu)點(diǎn):
1砚亭、算法可以自由切換渐裂。
2、避免使用多重條件判斷钠惩。
3柒凉、擴(kuò)展性良好。
缺點(diǎn):
1篓跛、策略類會(huì)增多膝捞。
2、所有策略類都需要對(duì)外暴露。
使用場(chǎng)景:
1蔬咬、如果在一個(gè)系統(tǒng)里面有許多類鲤遥,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動(dòng)態(tài)地讓一個(gè)對(duì)象在許多行為中選擇一種行為林艘。
2盖奈、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種。
3狐援、如果一個(gè)對(duì)象有很多的行為钢坦,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)啥酱。
CacheInterceptor實(shí)現(xiàn)了數(shù)據(jù)的選擇策略爹凹,來自網(wǎng)絡(luò)還是來自本地,這個(gè)場(chǎng)景也是比較契合策略模式場(chǎng)景镶殷,CacheInterceptor需要一個(gè)策略提供者提供它一個(gè)策略(錦囊)禾酱,CacheInterceptor根據(jù)這個(gè)策略去選擇走網(wǎng)絡(luò)數(shù)據(jù)還是本地緩存。
緩存的策略過程:
1绘趋、請(qǐng)求頭包含"If-Modified-Since"或"If-None-Match"暫時(shí)不走緩存颤陶。
2、客戶端通過cacheControl指定了無緩存陷遮,不走緩存滓走。
3、客戶端通過cacheControl指定了緩存拷呆,則看緩存過期時(shí)間闲坎,符合要求走緩存疫粥。
4茬斧、如果走了網(wǎng)絡(luò)請(qǐng)求,響應(yīng)狀態(tài)碼為304梗逮,只有客戶端請(qǐng)求頭包含"If-Modified-Since"或"If-None-Match"项秉,服務(wù)器數(shù)據(jù)沒變化的話會(huì)返回304狀態(tài)碼,不會(huì)返回響應(yīng)內(nèi)容慷彤,表示客戶端繼續(xù)用緩存娄蔼。
? ? ? ??
享元模式:
定義:嘗試重用現(xiàn)有的同類對(duì)象,如果未找到匹配的對(duì)象底哗,則創(chuàng)建新對(duì)象岁诉,主要用于減少創(chuàng)建對(duì)象的數(shù)量,以減少內(nèi)存占用和提高性能跋选。
優(yōu)點(diǎn):
1涕癣、大大減少對(duì)象的創(chuàng)建,降低系統(tǒng)的內(nèi)存前标,使效率提高坠韩。
缺點(diǎn):
1距潘、提高了系統(tǒng)的復(fù)雜度,需要分離出外部狀態(tài)和內(nèi)部狀態(tài)只搁,而且外部狀態(tài)具有固有化的性質(zhì)音比,不應(yīng)該隨著內(nèi)部狀態(tài)的變化而變化,否則會(huì)造成系統(tǒng)的混亂氢惋。
使用場(chǎng)景:
1洞翩、系統(tǒng)有大量相似對(duì)象。
2明肮、需要緩沖池的場(chǎng)景菱农。
3、通過Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>()存放網(wǎng)絡(luò)請(qǐng)求的相關(guān)對(duì)象柿估。
4循未、在Dispatcher的線程池中,所用到了享元模式秫舌,一個(gè)不限容量的線程池的妖,線程空閑時(shí)存活時(shí)間為60秒,線程池實(shí)現(xiàn)了對(duì)象復(fù)用足陨,降低線程創(chuàng)建開銷嫂粟,從設(shè)計(jì)模式上來講,使用了享元模式墨缘。
外觀模式(Facade):
定義:外觀模式又叫作門面模式星虹,是一種通過為多個(gè)復(fù)雜的子系統(tǒng)提供一個(gè)一致的接口,而使這些子系統(tǒng)更加容易被訪問的模式镊讼,該模式對(duì)外有一個(gè)統(tǒng)一接口宽涌,外部應(yīng)用程序不用關(guān)心內(nèi)部子系統(tǒng)的具體細(xì)節(jié),這樣會(huì)大大降低應(yīng)用程序的復(fù)雜度蝶棋,提高了程序的可維護(hù)性铡恕,隱藏系統(tǒng)的復(fù)雜性同仆,并向客戶端提供了一個(gè)客戶端可以訪問系統(tǒng)的接口详炬。
優(yōu)點(diǎn):(迪米特法則的典型應(yīng)用)
1齿税、降低了子系統(tǒng)與客戶端之間的耦合度,使得子系統(tǒng)的變化不會(huì)影響調(diào)用它的客戶類吃溅。
2溶诞、對(duì)客戶屏蔽了子系統(tǒng)組件,減少了客戶處理的對(duì)象數(shù)目决侈,并使得子系統(tǒng)使用起來更加容易螺垢。
3、降低了大型軟件系統(tǒng)中的編譯依賴性,簡(jiǎn)化了系統(tǒng)在不同平臺(tái)之間的移植過程甩苛,因?yàn)榫幾g一個(gè)子系統(tǒng)不會(huì)影響其他的子系統(tǒng)蹂楣,也不會(huì)影響外觀對(duì)象。
使用場(chǎng)景:OKHttpClient里面組合了很多的類對(duì)象讯蒲。其實(shí)是將OKHttp的很多功能模塊痊土,全部包裝進(jìn)這個(gè)類中,讓這個(gè)類單獨(dú)提供對(duì)外的API墨林,這種設(shè)計(jì)叫做外觀模式赁酝。
四:項(xiàng)目中的核心類分析
4.1、HttpServiceMethod詳解
4.2旭等、Dispatcher詳解
4.3酌呆、Interceptor詳解
4.4、其余次要類的簡(jiǎn)述
4.1搔耕、ServiceMethod與HttpServiceMethod詳解
解析:將接口方法的調(diào)用調(diào)整為HTTP調(diào)用隙袁。在使用Retrofit時(shí),需要自定義網(wǎng)絡(luò)請(qǐng)求接口類弃榨,并通過Create函數(shù)創(chuàng)建相關(guān)的網(wǎng)絡(luò)對(duì)象菩收,這里主要就使用到
HttpServiceMethod類了,我們可以把自定義的網(wǎng)絡(luò)請(qǐng)求接口的每個(gè)函數(shù)轉(zhuǎn)換為一個(gè)key對(duì)象Method鲸睛,value就是一個(gè)個(gè)HttpServiceMethod對(duì)象娜饵,Retrofit通過ConcurrentHashMap數(shù)據(jù)接口存放轉(zhuǎn)換后的鍵值對(duì),后續(xù)的網(wǎng)絡(luò)請(qǐng)求調(diào)用時(shí)官辈,實(shí)際就是通過調(diào)用HttpServiceMethod實(shí)現(xiàn)的箱舞。
4.2、Dispatcher解析
官方注釋:關(guān)于何時(shí)執(zhí)行異步請(qǐng)求的策略拳亿,每個(gè)調(diào)度程序都使用ExecutorService在內(nèi)部運(yùn)行調(diào)用晴股,如果您提供自己的執(zhí)行程序,它應(yīng)該能夠同時(shí)運(yùn)行配置的最大調(diào)用數(shù)风瘦。
同步請(qǐng)求:
異步請(qǐng)求:
當(dāng)正在執(zhí)行的任務(wù)未超過最大限制64队魏,同時(shí)runningCallsForHost(call) <maxRequestsPerHost 同一Host的請(qǐng)求不超過5個(gè)公般,則會(huì)添加到正在執(zhí)行隊(duì)列万搔,同時(shí)提交給線程池。否則先加入等待隊(duì)列官帘,加入線程池直接執(zhí)行沒啥好說的瞬雹,但是如果加入等待隊(duì)列后,就需要等待有空閑名額才開始執(zhí)行刽虹,因此每次執(zhí)行完一個(gè)請(qǐng)求后酗捌,都會(huì)調(diào)用分發(fā)器的Finished方法。
分發(fā)器的線程池:
SynchronousQueue使用此隊(duì)列意味著希望獲得最大并發(fā)量,因?yàn)闊o論如何胖缤,向線程池提交任務(wù)尚镰,往隊(duì)列提交任務(wù)都會(huì)失敗,而失敗后如果沒有空閑的非核心線程哪廓,就會(huì)檢查如果當(dāng)前線程池中的線程數(shù)未達(dá)到最大線程狗唉,則會(huì)新建線程執(zhí)行新提交的任務(wù),完全沒有任何等待涡真,唯一制約它的就是最大線程數(shù)的個(gè)數(shù)分俯。因此一般配合 Integer.MAX_VALUE就實(shí)現(xiàn)了真正的無等待。
Dispatcher是一個(gè)任務(wù)調(diào)度器哆料,它內(nèi)部維護(hù)了三個(gè)雙端隊(duì)列:
readyAsyncCalls:準(zhǔn)備運(yùn)行的異步請(qǐng)求
runningAsyncCalls:正在運(yùn)行的異步請(qǐng)求
runningSyncCalls:正在運(yùn)行的同步請(qǐng)求
同步請(qǐng)求的話缸剪,就直接把請(qǐng)求添加到正在運(yùn)行的同步請(qǐng)求隊(duì)列runningSyncCalls中,異步請(qǐng)求會(huì)做個(gè)判斷东亦,如果正在運(yùn)行的異步請(qǐng)求不超過64杏节,而且同一個(gè)host下的異步請(qǐng)求不得超過5個(gè)則將請(qǐng)求添加到正在運(yùn)行的同步請(qǐng)求隊(duì)列runningAsyncCalls中,并開始執(zhí)行請(qǐng)求典阵,否則就添加到readyAsyncCalls繼續(xù)等待拢锹。
4.3、Interceptor解析
官方解析:觀察萄喳、修改并可能使發(fā)出的請(qǐng)求和返回的相應(yīng)響應(yīng)短路卒稳,通常,攔截器會(huì)在請(qǐng)求或響應(yīng)上添加他巨、刪除或轉(zhuǎn)換標(biāo)頭充坑。
重試及重定向攔截器:
解析:本攔截器是整個(gè)責(zé)任鏈中的第一個(gè),這意味著它會(huì)是首次接觸到Request與最后接收到Response的角色染突,在這個(gè)攔截器中主要功能就是判斷是否需要重試與重定向捻爷,重試的前提是出現(xiàn)了RouteException或者IOException,一但在后續(xù)的攔截器執(zhí)行過程中出現(xiàn)這兩個(gè)異常份企,就會(huì)通過recover方法進(jìn)行判斷是否進(jìn)行連接重試也榄,重定向發(fā)生在重試的判定之后,如果不滿足重試的條件司志,還需要進(jìn)一步調(diào)用followUpRequest根據(jù)Response的響應(yīng)碼(當(dāng)然甜紫,如果直接請(qǐng)求失敗,Response都不存在就會(huì)拋出異常)骂远,followup最大發(fā)生20次囚霸。
橋接攔截器:
解析:連接應(yīng)用程序和服務(wù)器的橋梁,我們發(fā)出的請(qǐng)求將會(huì)經(jīng)過它的處理才能發(fā)給服務(wù)器激才,比如設(shè)置請(qǐng)求內(nèi)容長(zhǎng)度拓型,編碼额嘿,gzip壓縮,cookie等劣挫,獲取響應(yīng)后保存Cookie等操作册养。
緩存攔截器:
解析:在發(fā)出請(qǐng)求前,判斷是否命中緩存压固,如果命中則可以不請(qǐng)求捕儒,直接使用緩存的響應(yīng)。
1邓夕、如果從緩存獲取的Response是null刘莹,那就需要使用網(wǎng)絡(luò)請(qǐng)求獲取響應(yīng)。
2焚刚、如果是Https請(qǐng)求点弯,但是又丟失了握手信息,那也不能使用緩存矿咕,需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求抢肛。
3、如果判斷響應(yīng)碼不能緩存且響應(yīng)頭有no-store標(biāo)識(shí)碳柱,那就需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求捡絮。
4、如果請(qǐng)求頭有no-cache標(biāo)識(shí)或者有If-Modified-Since/If-None-Match 莲镣,那么需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求福稳。
5、如果響應(yīng)頭沒有no-cache標(biāo)識(shí)瑞侮,且緩存時(shí)間沒有超過極限時(shí)間的圆,那么可以使用緩存,不需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求半火。
6越妈、如果緩存過期了,判斷響應(yīng)頭是否設(shè)置Etag/Last-Modified/Date钮糖,沒有那就直接使用網(wǎng)絡(luò)請(qǐng)求梅掠,否則需要考慮服務(wù)器返回304,并且店归,只要需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求阎抒,請(qǐng)求頭中就不能包含only-if-cached,否則框架直接返回504娱节。
連接攔截器:
解析:這個(gè)攔截器中的所有實(shí)現(xiàn)都是為了獲得一份與目標(biāo)服務(wù)器的連接挠蛉,在這個(gè)連接上進(jìn)行HTTP數(shù)據(jù)的收發(fā)祭示。
請(qǐng)求服務(wù)器攔截器:
解析:真正發(fā)起HTTP請(qǐng)求肄满,并拿回響應(yīng)報(bào)文谴古。
網(wǎng)絡(luò)請(qǐng)求與攔截器圖解:
攔截器總結(jié):
1、整個(gè)OkHttp功能的實(shí)現(xiàn)就在這五個(gè)默認(rèn)的攔截器中稠歉,所以先理解攔截器模式的工作機(jī)制是先決條件掰担,這五個(gè)攔截器分別為: 重試攔截器、橋接攔截器怒炸、緩存攔截器带饱、連接攔截器、請(qǐng)求服務(wù)攔截器阅羹,
每一個(gè)攔截器負(fù)責(zé)的工作不一樣勺疼,就好像工廠流水線,最終經(jīng)過這五道工序捏鱼,就成了最終的產(chǎn)品执庐,但是與流水線不同的是,OkHttp中的攔截器每次發(fā)起請(qǐng)求都會(huì)在交給下一個(gè)攔截器之前干一些事情导梆,
在獲得了結(jié)果之后又干一些事情轨淌。整個(gè)過程在請(qǐng)求向是順序的,而響應(yīng)則是逆序看尼。
2递鹉、當(dāng)用戶發(fā)起一個(gè)請(qǐng)求后,會(huì)由任務(wù)分發(fā)器Dispatcher將請(qǐng)求包裝并交給重試攔截器處理藏斩。
3躏结、重試攔截器在交出(交給下一個(gè)攔截器)之前,負(fù)責(zé)判斷用戶是否取消了請(qǐng)求狰域,在獲得了結(jié)果之后窜觉,會(huì)根據(jù)響應(yīng)碼判斷是否需要重定向,如果滿足條件那么就會(huì)重啟執(zhí)行所有攔截器北专。
4禀挫、橋接攔截器在交出之前,負(fù)責(zé)將HTTP協(xié)議必備的請(qǐng)求頭加入其中(如:Host)并添加一些默認(rèn)的行為(如:GZIP壓縮)拓颓,在獲得了結(jié)果后语婴,調(diào)用保存cookie接口并解析GZIP數(shù)據(jù)。
5驶睦、緩存攔截器顧名思義砰左,交出之前讀取并判斷是否使用緩存,獲得結(jié)果后判斷是否緩存场航。
6缠导、連接攔截器在交出之前,負(fù)責(zé)找到或者新建一個(gè)連接溉痢,并獲得對(duì)應(yīng)的socket流僻造,在獲得結(jié)果后不進(jìn)行額外的處 理憋他。
7、請(qǐng)求服務(wù)器攔截器進(jìn)行真正的與服務(wù)器的通信髓削,向服務(wù)器發(fā)送數(shù)據(jù)竹挡,解析讀取的響應(yīng)數(shù)據(jù),在經(jīng)過了這一系列的流程后立膛,就完成了一次HTTP請(qǐng)求揪罕。
4.4、其余次要類的簡(jiǎn)述
OkHttpClient:通信的客戶端宝泵,用來統(tǒng)一管理發(fā)起請(qǐng)求與解析響應(yīng)好啰。
Call:Call是一個(gè)接口,它是HTTP請(qǐng)求的抽象描述儿奶,具體實(shí)現(xiàn)類是RealCall坎怪,它由CallFactory創(chuàng)建。
Request:請(qǐng)求廓握,封裝請(qǐng)求的具體信息搅窿,例如:url、header等隙券。
RequestBody:請(qǐng)求體男应,用來提交流、表單等請(qǐng)求信息娱仔。
Response:HTTP請(qǐng)求的響應(yīng)沐飘,獲取響應(yīng)信息,例如:響應(yīng)header等牲迫。
ResponseBody:HTTP請(qǐng)求的響應(yīng)體耐朴,被讀取一次以后就會(huì)關(guān)閉,所以我們重復(fù)調(diào)用responseBody.string()獲取請(qǐng)求結(jié)果是會(huì)報(bào)錯(cuò)的盹憎。
Interceptor:Interceptor是請(qǐng)求攔截器筛峭,負(fù)責(zé)攔截并處理請(qǐng)求,它將網(wǎng)絡(luò)請(qǐng)求陪每、緩存影晓、透明壓縮等功能都統(tǒng)一起來,每個(gè)功能都是一個(gè)Interceptor檩禾,所有的Interceptor最終連接成一個(gè)Interceptor.Chain挂签,典型的責(zé)任鏈模式實(shí)現(xiàn)。
StreamAllocation:用來控制Connections與Streas的資源分配與釋放盼产。
RouteSelector:選擇路線與自動(dòng)重連饵婆。
RouteDatabase:記錄連接失敗的Route黑名單。
五:常見問題匯總
5.1戏售、應(yīng)用攔截器和網(wǎng)絡(luò)攔截器的區(qū)別侨核?
解析:
應(yīng)用攔截器(Interceptor)
1草穆、不需要擔(dān)心中間過程的響應(yīng),如重定向和重試。
2芹关、總是只調(diào)用一次,即使HTTP響應(yīng)是從緩存中獲取续挟。
3紧卒、觀察應(yīng)用程序的初衷侥衬,不關(guān)心OkHttp注入的頭信息如: If-None-Match。
4跑芳、允許短路而不調(diào)用Chain.proceed()轴总,即中止調(diào)用。
5博个、允許重試怀樟,使Chain.proceed()調(diào)用多次。
6盆佣、Okhttp的調(diào)用的順序不一樣往堡。
網(wǎng)絡(luò)攔截器(Network interceptor)
1、能夠操作中間過程的響應(yīng)共耍,如重定向和重試虑灰。
2、當(dāng)網(wǎng)絡(luò)短路而返回緩存響應(yīng)時(shí)不被調(diào)用痹兜。
3穆咐、只觀察在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)。
4字旭、攜帶請(qǐng)求來訪問連接对湃。
5.2、Android中網(wǎng)絡(luò)請(qǐng)求框架的演進(jìn)過程遗淳?
? ? ??
5.3拍柒、OkHttp怎么實(shí)現(xiàn)連接池
為什么需要連接池:
頻繁的進(jìn)行建立Sokcet連接和斷開Socket是非常消耗網(wǎng)絡(luò)資源和浪費(fèi)時(shí)間的,所以HTTP中的keepalive連接對(duì)于降低延遲和提升速度有非常重要的作用屈暗,keepalive機(jī)制是什么呢斤儿,也就是可以在一次TCP連接中可以持續(xù)發(fā)送多份數(shù)據(jù)而不會(huì)斷開連接,所以連接的多次使用恐锦,也就是復(fù)用就變得格外重要了往果,而復(fù)用連接就需要對(duì)連接進(jìn)行管理,于是就有了連接池的概念一铅。
OkHttp中使用ConnectionPool實(shí)現(xiàn)連接池陕贮,默認(rèn)支持5個(gè)并發(fā)KeepAlive,默認(rèn)鏈路生命為5分鐘潘飘。
怎么實(shí)現(xiàn)連接池:
1肮之、首先掉缺,ConnectionPool中維護(hù)了一個(gè)雙端隊(duì)列Deque,也就是兩端都可以進(jìn)出的隊(duì)列戈擒,用來存儲(chǔ)連接眶明。
2、然后在ConnectInterceptor筐高,也就是負(fù)責(zé)建立連接的攔截器中搜囱,首先會(huì)找可用連接,也就是從連接池中去獲取連接柑土,具體的就是會(huì)調(diào)用到ConnectionPool的get方法蜀肘,也就是遍歷了雙端隊(duì)列,如果連接有效稽屏,就會(huì)調(diào)用acquire方法計(jì)數(shù)并返回這個(gè)連接扮宠。
3、如果沒找到可用連接狐榔,就會(huì)創(chuàng)建新連接坛增,并會(huì)把這個(gè)建立的連接加入到雙端隊(duì)列中,同時(shí)開始運(yùn)行線程池中的線程薄腻,其實(shí)就是調(diào)用了ConnectionPool的put方法收捣。
4、其實(shí)這個(gè)線程池中只有一個(gè)線程被廓,是用來清理連接的坏晦,也就是上述的cleanupRunnable。
5嫁乘、怎樣屬于空閑連接昆婿,一個(gè)方法acquire計(jì)數(shù)方法,在RealConnection中蜓斧,有一個(gè)StreamAllocation虛引用列表allocations仓蛆,每創(chuàng)建一個(gè)連接,就會(huì)把連接對(duì)應(yīng)的StreamAllocationReference添加進(jìn)該列表中挎春,如果連接關(guān)閉以后就將該對(duì)象移除看疙。
6、總結(jié)直奋,主要就是管理雙端隊(duì)列Deque<RealConnection>能庆,可以用的連接就直接用,然后定期清理連接脚线,同時(shí)通過對(duì)StreamAllocation的引用計(jì)數(shù)實(shí)現(xiàn)自動(dòng)回收搁胆。
5.4、如何支持自簽名網(wǎng)站https的訪問?
解析:通過鴻洋的開源項(xiàng)目okhttputils的工具類HttpsUtils渠旁,實(shí)現(xiàn)對(duì)SSLSocketFactory和X509TrustManager變量的設(shè)置攀例。
請(qǐng)求對(duì)象的設(shè)置代碼:
? ? ??
5.5、如何通過一張圖來整體匯總網(wǎng)絡(luò)通信的過程及重要知識(shí)點(diǎn)顾腊?
? ? ?
? ??
六:擴(kuò)展閱讀
1粤铭、https://blog.csdn.net/u012346890/article/details/111568776(Okhttp相關(guān)知識(shí)點(diǎn)總結(jié))
2、https://blog.csdn.net/RoiRoc/article/details/102858986?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.pc_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=2(OkHttp知識(shí)點(diǎn)總結(jié))
3杂靶、https://blog.csdn.net/weixin_43662090/article/details/114868599(okhttp學(xué)習(xí)系列之interceptor和network interceptor的區(qū)別)
4梆惯、https://blog.csdn.net/u012165769/article/details/109212157(Android 網(wǎng)絡(luò)框架之Retrofit源碼解析)
6伪煤、http://www.reibang.com/p/030222c4df4e(Proxy.newProxyInstance理解)
7加袋、https://blog.csdn.net/wuqiqi1992/article/details/108667577?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_default&utm_relevant_index=2(Android知識(shí)點(diǎn)整理11:Okhttp)
8凛辣、https://github.com/hongyangAndroid/okhttputils(okhttp的常見封裝實(shí)現(xiàn))