? ? ?app項目的開發(fā)過程中,都少不了進行網(wǎng)絡請求的功能模塊慎菲。而現(xiàn)在的主流網(wǎng)絡框架中洽损,使用最多而且反饋最好. 最穩(wěn)定的就是 retrofit+okhttp+rxjava(rxandroid)的組合框架了失暴。今天記錄一下在kotlin語言開發(fā)下實現(xiàn)該網(wǎng)絡框架的封裝過程昂拂。
1.網(wǎng)絡請求原理
? ? ? ? 無論使用何種語言進行開發(fā)托慨,其實只是在語法的編寫和語法糖的調用上有一些差異嘁圈。但是需要明確的是魏身,先要搞清楚這一套網(wǎng)絡框架的原理肛炮,包括框架中每個模塊的具體作用止吐。
1.1 okhttp?
? ? okhttp是最常用的網(wǎng)絡請求框架了宝踪,最新的4.x的版本已經由java全部換成了kotlin,api的使用與老的3.x的版本也有了一些差異碍扔,具體調用可以去git官網(wǎng)查看瘩燥,先來看一張它的調用流程圖
基本流程:
? ? 1.我們通過OkhttpClient創(chuàng)建一個Call,并發(fā)起同步或異步請求時不同;
????2.okhttp會通過Dispatcher對我們所有的RealCall(Call的具體實現(xiàn)類)進行統(tǒng)一管理颤芬,并通過execute()及enqueue()方法對同步或異步請求進行處理;
? ? 3.execute()及enqueue()這兩個方法會最終調用RealCall中的getResponseWithInterceptorChain()方法套鹅,從攔截器鏈中獲取返回結果;
? ?4.攔截器鏈中汰具,依次通過RetryAndFollowUpInterceptor(重定向攔截器)卓鹿、BridgeInterceptor(橋接攔截器)、CacheInterceptor(緩存攔截器)留荔、ConnectInterceptor(連接攔截器)吟孙、CallServerInterceptor(網(wǎng)絡攔截器)對請求依次處理,與服務的建立連接后聚蝶,獲取返回數(shù)據(jù)杰妓,再經過上述攔截器依次處理后,最后將結果返回給調用方碘勉。
? ? ?這其中不同的攔截器對應的不同的訪問情況巷挥,具體的介紹可以自行去官網(wǎng)了解,這里就不一一介紹了?验靡,而且okhttp的攔截器模式使得我們很容易添加一個自定義攔截器對請求和返回結果進行處理倍宾。
1.2 retrofit
? ? ? ? retrofit可以理解為在okhttp的基礎上進行加強再封裝的一個網(wǎng)絡框架,底層使用okhttp進行最終的訪問胜嗓,主要為了最大程度上優(yōu)化請求鏈接的參數(shù)構建和實現(xiàn)解耦高职,使用注解+java接口來定義后臺服務API接口。
注解主要分為 方法注解 和 參數(shù)注解
方法注解
@GET? ?表明HTTP請求方法為GET,(可選)注解的value屬性用來設置相對/絕對url
@POST? ?表明HTTP請求方法為POST,(可選)注解的value屬性用來設置相對/絕對url
@PUT? ? 表明HTTP請求方法為PUT,(可選)注解的value屬性用來設置相對/絕對url
@DELETE? ? 表明http請求方法為DELETE,(可選)注解的value屬性用來設置相對/絕對url
@PATCH? ?表明HTTP請求方法為PATCH,(可選)注解的value屬性用來設置相對/絕對url
@HEAD? ?表明HTTP請求方法為HEAD,(可選)注解的value屬性用來設置相對/絕對url
@OPTIONS? ?表明HTTP請求方法為OPTIONS,(可選)注解的value屬性用來設置相對/絕對url
@HTTP? 通過@HTTP注解指定http協(xié)議的請求方法,是否允許body,(可選)注解的value屬性用來設置相對/絕對url
@FormUrlEncoded? ?表明發(fā)起HTTP請求的RequestBody是form表單方式
@Multipart? ? 表明發(fā)起HTTP請求的RequestBody是Multipar方式
@Headers? ? 使用注解的value值數(shù)組作為HTTP請求的頭辞州,用于一些固定的Header參數(shù)
@Streaming? ? 用于需要直接返回流的函數(shù)
參數(shù)注解
@Url? ? HTTP請求的url路徑(相對/絕對),可以包含{path_holder},如:http://xxx.com/{user_holder}/detail
@Path? ?用于動態(tài)替換URL路徑中的path_holder
@Body? ?表明此參數(shù)用作HTTP請求的body
@Field? 表明此參數(shù)用作HTTP請求的form表單參數(shù)怔锌,key為注解的value值
@FieldMap? ?以map形式傳入的form表單參數(shù)
@Header? ?表明此參數(shù)用作HTTP請求的header,key為注解的value值
@HeaderMap? ?以map形式傳入的多個header鍵值對
@Part? ?表明參數(shù)為Http的multipart參數(shù)之一
@PartMap? 以map形式傳入的multipart參數(shù)表
@Query GET方法的query參數(shù)变过,用于拼接完整請求路徑
@QueryMap? ?以map傳入的GET方法的query參數(shù)埃元,用于拼接完整請求路徑
? ? ? 了解了注解之后,開始介紹Retrofit.create函數(shù)創(chuàng)建接口動態(tài)代理生成一個自定義的接口的實現(xiàn)類的過程
1 加載對應method的ServiceMethod實例
2 使用ServiceMethod實例和方法調用參數(shù)創(chuàng)建OkHttpCall
3 調用serviceMethod.callAdapter.adapt(okHttpCall)來產生method所定義的返回(Call<T>或者其他自定義CallAdapter支持的返回)
這其中以下4個元素是比較重要的媚狰,也是retrofit功能強大的保證亚情,其中包括?callFactory,callAdapter哈雏,responseConverter 楞件,parameterHandlers?
? ??callFactory是用來創(chuàng)建真正要執(zhí)行的okhttp3.Call的工廠類衫生,可以Retrofit.Builder中設置,如果不設置土浸,默認會new一個OkHttpClient作為callFactory
callAdapter是用來最終處理OkHttpCall實例并返回接口Method所定義的返回
responseConverter 用來將Http請求的結果轉換成接口Method所定義的結果(return或者Callback<T>中的T)
parameterHandlers 根據(jù)接口Method參數(shù)的注解所生成的參數(shù)處理Handler數(shù)組
1.3? rxjava
? ? ? ? rxjava 其實是一個實現(xiàn)異步操作罪针,基于時間本身的第三方庫,沒有這個庫之前黄伊,我們執(zhí)行異步操作可以通過handler或者是tread進行泪酱,rajva等于就是針對這一類型事務處理的加強版本,那相對于傳統(tǒng)實現(xiàn)異步操作的功能还最,rxjava的好處或者是優(yōu)勢點在哪里呢墓阀?先來看看它的基本原理和使用。
? ? rxjava采用的是觀察者模式拓轻,有四個基本概念:Observable (可觀察者斯撮,即被觀察者)、 Observer (觀察者)扶叉、 subscribe (訂閱)勿锅、事件。Observable 和 Observer 通過 subscribe() 方法實現(xiàn)訂閱關系枣氧,從而 Observable 可以在需要的時候發(fā)出事件來通知 Observer溢十。
RxJava 的事件回調方法除了普通事件 onNext() (相當于 onClick() / onEvent())之外,還定義了兩個特殊的事件:onCompleted() 和 onError()达吞。
onCompleted(): 事件隊列完結张弛。RxJava 不僅把每個事件單獨處理,還會把它們看做一個隊列酪劫。RxJava 規(guī)定乌庶,當不會再有新的 onNext() 發(fā)出時,需要觸發(fā)
onCompleted() 方法作為標志契耿。
onError(): 事件隊列異常瞒大。在事件處理過程中出異常時,onError() 會被觸發(fā)搪桂,同時隊列自動終止透敌,不允許再有事件發(fā)出。
在一個正確運行的事件序列中, onCompleted() 和 onError() 有且只有一個踢械,并且是事件序列中的最后一個酗电。需要注意的是,onCompleted() 和 onError() 二者也是互斥的内列,即在隊列中調用了其中一個撵术,就不應該再調用另一個。
????在 RxJava 的默認規(guī)則中话瞧,事件的發(fā)出和消費都是在同一個線程的嫩与。也就是說寝姿,如果只用上面的方法,實現(xiàn)出來的只是一個同步的觀察者模式划滋。觀察者模式本身的目的就是『后臺處理饵筑,前臺回調』的異步機制,因此異步對于 RxJava 是至關重要的处坪。而要實現(xiàn)異步根资,則需要用到 RxJava 的另一個線程切換概念: Scheduler ,在不指定線程的情況下同窘, RxJava 遵循的是線程不變的原則玄帕,即:在哪個線程調用 subscribe(),就在哪個線程生產事件想邦;在哪個線程生產事件裤纹,就在哪個線程消費事件。如果需要切換線程案狠,就需要用到 Scheduler (調度器)。RxJava 已經內置了幾個 Scheduler?
????Schedulers.newThread(): 總是啟用新線程钱雷,并在新線程執(zhí)行操作骂铁。
????Schedulers.io(): I/O 操作(讀寫文件、讀寫數(shù)據(jù)庫罩抗、網(wǎng)絡信息交互等)所使用的 Scheduler拉庵。行為模式和 newThread() 差不多,區(qū)別在于 io() 的內部實現(xiàn)是是用一個無數(shù)量上限的線程池套蒂,可以重用空閑的線程钞支,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中操刀,可以避免創(chuàng)建不必要的線程烁挟。
????Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算骨坑,即不會被 I/O 等操作限制性能的操作撼嗓,例如圖形的計算。這個 Scheduler 使用的固定的線程池欢唾,大小為 CPU 核數(shù)且警。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU礁遣。
? ? ?AndroidSchedulers.mainThread()斑芜,它指定的操作將在 Android 主線程運行
有了這幾個 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了祟霍。
subscribeOn(): 指定 subscribe() 所發(fā)生的線程杏头,即 Observable.OnSubscribe 被激活時所處的線程盈包。或者叫做事件產生的線程大州。
observeOn(): 指定 Subscriber 所運行在的線程续语。或者叫做事件消費的線程厦画。
? ?2 具體實現(xiàn)
? ? ? ?基本模塊的功能簡單的介紹完成了疮茄,現(xiàn)在我們開始組合它們,將它們分裝成通用的網(wǎng)絡請求模塊 根暑,首先我們需要在gradle文件中添加對應的遠程倉庫依賴力试。
? ? 代碼中首先實現(xiàn)okhttpclient請求類,常用的攔截器排嫌,連接時長等配置畸裳。
? ?這樣最基本的一個ohttp模塊構建就完成了,接下來要開始進行retrofit模塊的處理淳地,既然是通用的請求模塊怖糊,那就需要使用泛型來實現(xiàn)統(tǒng)一的retrofit請求模塊的方法,其中包含有對應的gson解析工廠颇象,rxjava的適配器伍伤,設置最基本的okhttpclient模塊。
? ? 目前這一步遣钳,已經完成了網(wǎng)絡框架的大部分功能了扰魂,但是我們知道常規(guī)的網(wǎng)絡請求或者去數(shù)據(jù)時,為了保證app使用過程的流暢蕴茴,一般都是異步回調的操作劝评。,所以接下來要實現(xiàn)rxjava的相關功能倦淀。
到了這一步蒋畜,很多人有疑問說沒見到具體的網(wǎng)絡請求包含在這里面。其實這正是rxjava的關鍵作用撞叽,它的主要作用只是設置觀察者和被觀察者百侧,進行事件的訂閱而已,我們最后看看具體事例使用能扒。
這樣來看佣渴,就把rxjava和retrofit關聯(lián)在一起了,最終實現(xiàn)了異步回調的網(wǎng)絡請求. 代碼很簡單初斑,只是為了熟悉和掌握個功能的模塊及原理辛润,在此做個筆記記錄一下。