可怕之碗!RxHttp2.0重大更新!協(xié)程發(fā)請(qǐng)求季希,僅需三步

1褪那、前言

RxHttp 在v2.0版本中加入對(duì)協(xié)程的支持幽纷,收到了廣大kotlin用戶的喜愛,他們也不禁感慨博敬,原來協(xié)程發(fā)請(qǐng)求還能如此優(yōu)雅友浸,比retrofit強(qiáng)大的不止一點(diǎn)點(diǎn),然而偏窝,這就夠了嗎收恢?遠(yuǎn)遠(yuǎn)不夠,為啥囚枪,因?yàn)檫€有痛點(diǎn)沒解決派诬,為此,我也收集幾個(gè)目前網(wǎng)絡(luò)請(qǐng)求遇到的痛點(diǎn)链沼,如下:

  • 異步操作默赂,協(xié)程已為我們提供了async操作符處理異步問題,但用到時(shí)括勺,每次還要包裝一次缆八,不能接受
  • 超時(shí)與重試,這種情況遇到的不多疾捍,但幾乎每個(gè)開發(fā)者都會(huì)遇到奈辰,真遇到時(shí),如果沒有對(duì)應(yīng)的API乱豆,也著實(shí)讓人著急
  • 請(qǐng)求開始/結(jié)束延遲奖恰,這種情況也不多,但遇到的人也不少宛裕,自己處理著實(shí)麻煩
  • 在請(qǐng)求并行中瑟啃,假設(shè)有A、B兩個(gè)請(qǐng)求甚至更多揩尸,它們互不依賴蛹屿,然而在協(xié)程中,如果A請(qǐng)求出現(xiàn)異常岩榆,那么協(xié)程就中止了错负,此時(shí)B也跟著中止了,這是我們不想看到的結(jié)果勇边,如何解決犹撒?常規(guī)的做法是對(duì)每個(gè)請(qǐng)求都做異常處理,使得出現(xiàn)異常粒褒,協(xié)程不會(huì)結(jié)束识颊。但每個(gè)請(qǐng)求都需要單獨(dú)處理,寫起來著實(shí)會(huì)讓人抓破頭皮怀浆,這是很大的痛點(diǎn)

等等谊囚,其實(shí)還有很多小細(xì)節(jié)的問題,這里就就不一一列舉了执赡。

正因有以上問題镰踏,所以RxHttp v2.2.0版本就來了,該版本主要改動(dòng)如下

  • 新增一系列非常好用的操作符沙合,如:asysn奠伪、timeoutretry首懈、tryAwait等等
  • 完全剔除RxJava绊率,采用外掛方法替代,也正因如此究履,RxHttp做到同時(shí)支持RxJava2與RxJava3
  • RxLieScope提取為單獨(dú)的一個(gè)庫滤否,專門處理協(xié)程開啟/關(guān)閉/異常處理,本文后續(xù)會(huì)單獨(dú)介紹

gradle依賴

dependencies {
   //必須
   implementation 'com.ljx.rxhttp:rxhttp:2.2.1'
   kapt 'com.ljx.rxhttp:rxhttp-compiler:2.2.1' //生成RxHttp類

   //以下均為非必須
   //管理協(xié)程生命周期最仑,頁面銷毀藐俺,關(guān)閉請(qǐng)求
   implementation 'com.ljx.rxlife:rxlife-coroutine:2.0.0'  

   //Converter 根據(jù)自己需求選擇 RxHttp默認(rèn)內(nèi)置了GsonConverter
   implementation 'com.ljx.rxhttp:converter-jackson:2.2.1'
   implementation 'com.ljx.rxhttp:converter-fastjson:2.2.1'
   implementation 'com.ljx.rxhttp:converter-protobuf:2.2.1'
   implementation 'com.ljx.rxhttp:converter-simplexml:2.2.1'
}

注:純Java項(xiàng)目,請(qǐng)使用annotationProcessor替代kapt泥彤;依賴完欲芹,記得rebuild,才會(huì)生成RxHttp類

歡迎加入RxHttp&RxLife交流群:378530627

2吟吝、請(qǐng)求三部曲

相信還有沒了解過RxHttp的同學(xué)菱父,這里貼出RxHttp請(qǐng)求流程圖,記住該圖剑逃,你就掌握了RxHttp的精髓浙宜,如下:


image

代碼表示

val str = RxHttp.get("/service/...") //第一步,確定請(qǐng)求方式炕贵,可以選擇postForm梆奈、postJson等方法
    .toStr()    //第二步,確認(rèn)返回類型称开,這里代表返回String類型
    .await()    //第二步亩钟,使用await方法拿到返回值

怎么樣,是不是非常簡(jiǎn)單鳖轰?

3清酥、RxHttp操作符

3.1、retry 失敗重試

該操作符非常強(qiáng)大蕴侣,不僅做到了失敗重試焰轻,還做到了周期性失敗重試,即間隔幾秒后重試昆雀,來看下完整的方法簽名

/**
 * 失敗重試辱志,該方法僅在使用協(xié)程時(shí)才有效
 * @param times  重試次數(shù), 默認(rèn)Int.MAX_VALUE 代表不斷重試
 * @param period 重試周期, 默認(rèn)為0, 單位: milliseconds
 * @param test   重試條件, 默認(rèn)為空蝠筑,即無條件重試
 */
fun retry(
    times: Int = Int.MAX_VALUE,
    period: Long = 0,
    test: ((Throwable) -> Boolean)? = null
)

retry()方法共有3個(gè)參數(shù),分別是重試次數(shù)揩懒、重試周期什乙、重試條件,都有默認(rèn)值已球,3個(gè)參數(shù)可以隨意搭配臣镣,如:

retry()    //無條件、不間斷智亮、一直重試
retry(2)   //無條件忆某、不間斷、重試2次
retry(2, 1000)   //無條件 間隔1s 重試2次
retry { it is ConnectException } //有條件阔蛉、不間斷弃舒、一直重試
retry(2) { it is ConnectException }  //有條件、不間斷馍忽、重試2次
retry(2, 1000) { it is ConnectException }  //有條件棒坏、間隔1s、重試2次
retry(period = 1000) { it is ConnectException } //有條件遭笋、間斷1s坝冕、一直重試

前兩個(gè)參數(shù)相信大家一看就能明白,這里對(duì)第3個(gè)參數(shù)額外說一下瓦呼,通過第三個(gè)參數(shù)喂窟,我們可以拿到Throwable異常對(duì)象,我們可以對(duì)異常做判斷央串,如果需要重試磨澡,就返回true,不需要就返回false质和,下面看看具體代碼

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .retry(2, 1000) {       //重試2次稳摄,每次間隔1s                       
        it is ConnectException   //如果是網(wǎng)絡(luò)異常就重試     
    }                                             
    .await()                     

3.2、timeout 超時(shí)

OkHttp提供了全局的讀饲宿、寫及連接超時(shí)厦酬,有時(shí)我們也需要為某個(gè)請(qǐng)求設(shè)置不同的超時(shí)時(shí)長,此時(shí)就可以用到RxHttp的timeout(Long)方法瘫想,如下:

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(3000)      //超時(shí)時(shí)長為3s                           
    .await()                       

3.3仗阅、async 異步操作符

如果我們有兩個(gè)請(qǐng)求需要并行時(shí),就可以使用該操作符国夜,如下:

//同時(shí)獲取兩個(gè)學(xué)生信息
suspend void initData() {
  val asyncStudent1 = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .async(this)   //this為CoroutineScope對(duì)象减噪,這里會(huì)返回Deferred<Student>   
    
  val asyncStudent2 = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .async(this)   //this為CoroutineScope對(duì)象,這里會(huì)返回Deferred<Student>   

  //隨后調(diào)用await方法獲取對(duì)象    
  val student1 = asyncStudent1.await()
  val student2 = asyncStudent2.await()
} 

3.4、delay筹裕、startDelay 延遲

delay操作符是請(qǐng)求結(jié)束后醋闭,延遲一段時(shí)間返回;而startDelay操作符則是延遲一段時(shí)間后再發(fā)送請(qǐng)求朝卒,如下:

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .delay(1000)      //請(qǐng)求回來后目尖,延遲1s返回                         
    .await()       
    
val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .startDelay(1000)     //延遲1s后再發(fā)送請(qǐng)求       
    .await()     

3.5、onErrorReturn扎运、onErrorReturnItem異常默認(rèn)值

有些情況,我們不希望請(qǐng)求出現(xiàn)異常時(shí)饮戳,直接走異澈乐危回調(diào),此時(shí)我們就可以通過兩個(gè)操作符扯罐,給出默認(rèn)的值负拟,如下:

//根據(jù)異常給出默認(rèn)值
val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(100)      //超時(shí)時(shí)長為100毫秒  
    .onErrorReturn {
        //如果是超時(shí)異常,就給出默認(rèn)值歹河,否則掩浙,拋出原異常
        return@onErrorReturn if (it is TimeoutCancellationException)
            Student()                                              
        else                                                        
            throw it                                                
    }
    .await()
    
//只要出現(xiàn)異常,就返回默認(rèn)值
val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(100)      //超時(shí)時(shí)長為100毫秒  
    .onErrorReturnItem(Student())
    .await()

3.6秸歧、tryAwait 異常返回null

如果你不想在異常時(shí)返回默認(rèn)值厨姚,又不想異常是影響程序的執(zhí)行,tryAwait就派上用場(chǎng)了键菱,它會(huì)在異常出現(xiàn)時(shí)谬墙,返回null,如下:

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(100)      //超時(shí)時(shí)長為100毫秒                        
    .tryAwait()     //這里返回 Student? 對(duì)象经备,即有可能為空  

3.7拭抬、map 轉(zhuǎn)換符號(hào)

map操作符很好理解,RxJava即協(xié)程的Flow都有該操作符侵蒙,功能都是一樣造虎,用于轉(zhuǎn)換對(duì)象,如下:

val student = RxHttp.postForm("/service/...")
    .toStr()
    .map { it.length }  //String轉(zhuǎn)Int                        
    .tryAwait()     //這里返回 Student? 對(duì)象纷闺,即有可能為空  

3.8算凿、以上操作符隨意搭配

以上操作符,可隨意搭配使用急但,但調(diào)用順序的不同澎媒,產(chǎn)生的效果也不一樣,這里悄悄告訴大家波桩,以上操作符只會(huì)對(duì)上游代碼產(chǎn)生影響戒努。

timeout及retry

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(50)
    .retry(2, 1000) { it is TimeoutCancellationException }                                  
    .await()                       

以上代碼,只要出現(xiàn)超時(shí),就會(huì)重試储玫,并且最多重試兩次侍筛。

但如果timeoutretry互換下位置撒穷,就不一樣了匣椰,如下:

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .retry(2, 1000) { it is TimeoutCancellationException }       
    .timeout(50)                                  
    .await()                       

此時(shí),如果50毫秒內(nèi)請(qǐng)求沒有完成端礼,就會(huì)觸發(fā)超時(shí)異常禽笑,并且直接走異常回調(diào)蛤奥,不會(huì)重試佳镜。為什么會(huì)這樣?原因很簡(jiǎn)單凡桥,timeout及retry操作符蟀伸,僅對(duì)上游代碼生效。如retry操作符缅刽,下游的異常是捕獲不到的啊掏,這就是為什么timeout在retry下,超時(shí)時(shí)衰猛,重試機(jī)制沒有觸發(fā)的原因迟蜜。

再看timeoutstartDelay操作符

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .startDelay(2000)       
    .timeout(1000)                                  
    .await()                       

以上代碼,必定會(huì)觸發(fā)超時(shí)異常啡省,因?yàn)閟tartDelay小泉,延遲了2000毫秒,而超時(shí)時(shí)長只有1000毫秒冕杠,所以必定觸發(fā)超時(shí)微姊。
但互換下位置,又不一樣了分预,如下:

val student = RxHttp.postForm("/service/...")
    .toClass<Student>()
    .timeout(1000)    
    .startDelay(2000)       
    .await()                       

以上代碼正常情況下兢交,都能正確拿到返回值,為什么笼痹?原因很簡(jiǎn)單配喳,上面說過,操作符只會(huì)對(duì)上游產(chǎn)生影響凳干,下游的startDelay延遲晴裹,它是不管的,也管不到救赐。

4涧团、協(xié)程開啟/關(guān)閉/異常處理

在以上示例中,我們統(tǒng)一用到await/tryAwait操作符獲取請(qǐng)求返回值,它們都是suspend掛起函數(shù)泌绣,需要在另一個(gè)suspend掛起函數(shù)或者協(xié)程中才能被調(diào)用钮追,故我們提供了RxLifeScope庫來處理協(xié)程開啟、關(guān)閉及異常處理阿迈,用法如下:

在FragemntActivity/Fragment/ViewModel環(huán)境下

在該環(huán)境下元媚,直接調(diào)用rxLifeScope對(duì)象的lanuch方法開啟協(xié)程即可,如下:

rxLifeScope.lanuch({
    //協(xié)程代碼塊苗沧,運(yùn)行在UI線程
    val student = RxHttp.postForm("/service/...")
    .toClass<Student>()                      
    .await()   
    //可直接更新UI
}, {
    //異晨兀回調(diào),這里可以拿到Throwable對(duì)象    
})

以上代碼待逞,會(huì)在頁面銷毀時(shí)鞠绰,自動(dòng)關(guān)閉協(xié)程,同時(shí)自動(dòng)關(guān)閉請(qǐng)求飒焦,無需擔(dān)心內(nèi)存泄露問題

非FragemntActivity/Fragment/ViewModel環(huán)境下

該環(huán)境下,我們需要手動(dòng)創(chuàng)建RxLifeScope對(duì)象屿笼,隨后調(diào)用lanuch方法開啟協(xié)程

val job = RxLifeScope().lanuch({
    //協(xié)程代碼塊牺荠,運(yùn)行在UI線程
    val student = RxHttp.postForm("/service/...")
    .toClass<Student>()                      
    .await()   
    //可直接更新UI
}, {
    //異常回調(diào)驴一,這里可以拿到Throwable對(duì)象  
})

//在合適的時(shí)機(jī)關(guān)閉協(xié)程
job.cancel()

以上代碼休雌,由于未與生命周期綁定,故我們需要在合適的時(shí)機(jī)肝断,手動(dòng)關(guān)閉協(xié)程杈曲,協(xié)程關(guān)閉,請(qǐng)求也會(huì)跟著關(guān)閉

監(jiān)聽協(xié)程開啟/結(jié)束回調(diào)

以上我們?cè)?code>lanuch方法胸懈,傳入?yún)f(xié)程運(yùn)行回調(diào)及異车F耍回調(diào),我們也可以傳入?yún)f(xié)程開啟及結(jié)束回調(diào)趣钱,如下:

rxLifeScope.launch({                                      
    //協(xié)程代碼塊                                              
    val student = RxHttp.postForm("/service/...")
        .toClass<Student>()
        .await()   
    //可直接更新UI                   
}, {                                                      
    //異秤肯祝回調(diào),這里可以拿到Throwable對(duì)象首有,運(yùn)行在UI線程                                       
}, {                                                     
    //開始回調(diào)燕垃,可以開啟等待彈窗,運(yùn)行在UI線程                                     
}, {                                                     
    //結(jié)束回調(diào)井联,可以銷毀等待彈窗卜壕,運(yùn)行在UI線程                                                  
})                                                       

以上回調(diào),均運(yùn)行在UI線程

5烙常、小結(jié)

可以看到轴捎,前面文章開頭提到超時(shí)/重試問題,就用timeout/retry,延遲就用delay/startDelay轮蜕,出現(xiàn)異常不想中斷協(xié)程的運(yùn)行昨悼,就用onErrorReturn/onErrorReturnItem或者tryAwait,總之跃洛,一切都是那么的優(yōu)雅率触。

RxHttp的優(yōu)雅遠(yuǎn)不止這些,BaseUrl的處理汇竭,文件上傳/下載/進(jìn)度監(jiān)聽葱蝗,緩存處理、業(yè)務(wù)code統(tǒng)一判斷等等细燎,處理的都令人嘆為觀止两曼,

更多功能查看以下文章

協(xié)程用法:RxHttp ,比Retrofit 更優(yōu)雅的協(xié)程體驗(yàn)

RxJava用法:RxHttp 讓你眼前一亮的Http請(qǐng)求框架

RxHttp 全網(wǎng)Http緩存最優(yōu)解

最后玻驻,開源不易悼凑,寫文章更不易,如果你覺得不錯(cuò)RxHttp或給你帶來了幫助璧瞬,歡迎點(diǎn)贊收藏户辫,以備不時(shí)之需,如果可以嗤锉,再給個(gè)star渔欢,我將感激不盡,????????????????????????

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘟忱,一起剝皮案震驚了整個(gè)濱河市奥额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌访诱,老刑警劉巖垫挨,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異触菜,居然都是意外死亡棒拂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門玫氢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帚屉,“玉大人,你說我怎么就攤上這事漾峡」サ” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵生逸,是天一觀的道長牢屋。 經(jīng)常有香客問我且预,道長,這世上最難降的妖魔是什么烙无? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任锋谐,我火速辦了婚禮,結(jié)果婚禮上截酷,老公的妹妹穿的比我還像新娘涮拗。我一直安慰自己,他們只是感情好迂苛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布三热。 她就那樣靜靜地躺著,像睡著了一般三幻。 火紅的嫁衣襯著肌膚如雪就漾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天念搬,我揣著相機(jī)與錄音抑堡,去河邊找鬼。 笑死朗徊,一個(gè)胖子當(dāng)著我的面吹牛首妖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荣倾,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼骑丸!你這毒婦竟也來了舌仍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤通危,失蹤者是張志新(化名)和其女友劉穎铸豁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菊碟,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节芥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逆害。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片头镊。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖魄幕,靈堂內(nèi)的尸體忽然破棺而出相艇,到底是詐尸還是另有隱情,我是刑警寧澤纯陨,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布坛芽,位于F島的核電站留储,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咙轩。R本人自食惡果不足惜获讳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望活喊。 院中可真熱鬧丐膝,春花似錦、人聲如沸胧弛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结缚。三九已至损晤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間红竭,已是汗流浹背尤勋。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茵宪,地道東北人最冰。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像稀火,于是被迫代替她去往敵國和親暖哨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359