可怕!RxHttp2.0重大更新!協程發請求,僅需三步

1、前言

RxHttp

在v2.0版本中加入對協程的支持,收到了廣大kotlin用戶的喜愛,他們也不禁感慨,原來協程發請求還能如此優雅,比retrofit強大的不止一點點,然而,這就夠了嗎?遠遠不夠,為啥,因為還有痛點沒解決,為此,我也收集幾個目前網絡請求遇到的痛點,如下:

  • 異步操作,協程已為我們提供了async操作符處理異步問題,但用到時,每次還要包裝一次,不能接受
  • 超時與重試,這種情況遇到的不多,但幾乎每個開發者都會遇到,真遇到時,如果沒有對應的API,也著實讓人著急
  • 請求開始/結束延遲,這種情況也不多,但遇到的人也不少,自己處理著實麻煩
  • 在請求並行中,假設有A、B兩個請求甚至更多,它們互不依賴,然而在協程中,如果A請求出現異常,那麼協程就中止了,此時B也跟著中止了,這是我們不想看到的結果,如何解決?常規的做法是對每個請求都做異常處理,使得出現異常,協程不會結束。但每個請求都需要單獨處理,寫起來著實會讓人抓破頭皮,這是很大的痛點

等等,其實還有很多小細節的問題,這裡就就不一一列舉了。

正因有以上問題,所以RxHttp v2.2.0版本就來了,該版本主要改動如下

  • 新增一系列非常好用的操作符,如:asysntimeoutretrytryAwait等等
  • 完全剔除RxJava,採用外掛方法替代,也正因如此,RxHttp做到同時支持RxJava2與RxJava3
  • 將RxLieScope提取為單獨的一個庫,專門處理協程開啟/關閉/異常處理,本文後續會單獨介紹

gradle依賴

<code>dependencies {
    
   implementation 

'com.ljx.rxhttp:rxhttp:2.2.1'

kapt

'com.ljx.rxhttp:rxhttp-compiler:2.2.1'

implementation

'com.ljx.rxlife:rxlife-coroutine:2.0.0'

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'

}/<code>

注:

1、純Java項目,請使用annotationProcessor替代kapt;

2、依賴完,記得rebuild,才會生成RxHttp類

3、源碼地址:
https://github.com/liujingxing/okhttp-RxHttp

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

2、請求三部曲

相信還有之前沒了解過RxHttp的同學,這裡貼出RxHttp請求流程圖,記住該圖,你就掌握了RxHttp的精髓,如下:


可怕!RxHttp2.0重大更新!協程發請求,僅需三步

代碼表示

<code>val str = RxHttp.

get

(

"/service/..."

) .toStr() .

await

() /<code>

怎麼樣,是不是非常簡單?


3、RxHttp操作符

3.1、retry 失敗重試

該操作符非常強大,不僅做到了失敗重試,還做到了週期性失敗重試,即間隔幾秒後重試,來看下完整的方法簽名

<code> 

fun

retry

( times:

Int

=

Int

.MAX_VALUE, period:

Long

=

0

, test: ((

Throwable

) ->

Boolean

)? =

null

)

/<code>

retry()方法共有3個參數,分別是重試次數、重試周期、重試條件,都有默認值,3個參數可以隨意搭配,如:

<code>

retry

()

retry

(

2

)

retry

(

2

,

1000

)

retry

{

it

is

ConnectException

}

retry

(

2

) {

it

is

ConnectException

}

retry

(

2

,

1000

) {

it

is

ConnectException

}

retry

(period =

1000

) {

it

is

ConnectException

} /<code>

前兩個參數相信大家一看就能明白,這裡對第3個參數額外說一下,通過第三個參數,我們可以拿到Throwable異常對象,我們可以對異常做判斷,如果需要重試,就返回true,不需要就返回false,下面看看具體代碼

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .retry(

2

,

1000

) { it

is

ConnectException } .

await

() /<code>

3.2、timeout 超時

OkHttp提供了全局的讀、寫及連接超時,有時我們也需要為某個請求設置不同的超時時長,此時就可以用到RxHttp的timeout(Long)

方法,如下

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

3000

) .

await

() /<code>

3.3、async 異步操作符

如果我們有兩個請求需要並行時,就可以使用該操作符,如下:

<code> 

suspend

void initData() {

val

asyncStudent1 = RxHttp.postForm(

"/service/..."

) .toClass() .async(

this

)

val

asyncStudent2 = RxHttp.postForm(

"/service/..."

) .toClass() .async(

this

)

val

student1 = asyncStudent1.await()

val

student2 = asyncStudent2.await() } /<code>

3.4、delay、startDelay 延遲

delay操作符是請求結束後,延遲一段時間返回;而startDelay操作符則是延遲一段時間後再發送請求,如下:

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .delay(

1000

) .

await

() val student = RxHttp.postForm(

"/service/..."

) .toClass() .startDelay(

1000

) .

await

() /<code>

3.5、onErrorReturn、onErrorReturnItem異常默認值

有些情況,我們不希望請求出現異常時,直接走異常回調,此時我們就可以通過兩個操作符,給出默認的值,如下:

<code> 

val

student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

100

) .onErrorReturn {

return

@onErrorReturn

if

(it

is

TimeoutCancellationException) Student()

else

throw

it } .await()

val

student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

100

) .onErrorReturnItem(Student()) .await()/<code>

3.6、tryAwait 異常返回null

如果你不想在異常時返回默認值,又不想異常是影響程序的執行,tryAwait就派上用場了,它會在異常出現時,返回null,如下:

<code>

val

student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

100

) .tryAwait() /<code>

3.7、map 轉換符號

map操作符很好理解,RxJava即協程的Flow都有該操作符,功能都是一樣,用於轉換對象,如下:

<code>val student = RxHttp.postForm(

"/service/..."

) .toStr() .

map

{ it.length } .tryAwait() /<code>

3.8、以上操作符隨意搭配

以上操作符,可隨意搭配使用,但調用順序的不同,產生的效果也不一樣,這裡悄悄告訴大家,以上操作符只會對上游代碼產生影響。

如timeout及retry:

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

50

) .retry(

2

,

1000

) { it

is

TimeoutCancellationException } .

await

() /<code>

以上代碼,只要出現超時,就會重試,並且最多重試兩次。

但如果timeout、retry互換下位置,就不一樣了,如下:

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .retry(

2

,

1000

) { it

is

TimeoutCancellationException } .timeout(

50

) .

await

() /<code>

此時,如果50毫秒內請求沒有完成,就會觸發超時異常,並且直接走異常回調,不會重試。為什麼會這樣?原因很簡單,timeout及retry操作符,僅對上游代碼生效。如retry操作符,下游的異常是捕獲不到的,這就是為什麼timeout在retry下,超時時,重試機制沒有觸發的原因。

再看timeout和startDelay操作符

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .startDelay(

2000

) .timeout(

1000

) .

await

() /<code>

以上代碼,必定會觸發超時異常,因為startDelay,延遲了2000毫秒,而超時時長只有1000毫秒,所以必定觸發超時。

但互換下位置,又不一樣了,如下:

<code>val student = RxHttp.postForm(

"/service/..."

) .toClass() .timeout(

1000

) .startDelay(

2000

) .

await

() /<code>

以上代碼正常情況下,都能正確拿到返回值,為什麼?原因很簡單,上面說過,操作符只會對上游產生影響,下游的startDelay延遲,它是不管的,也管不到。

4、協程開啟/關閉/異常處理

在以上示例中,我們統一用到await/tryAwait操作符獲取請求返回值,它們都是suspend

掛起函數,需要在另一個suspend掛起函數或者協程中才能被調用,故我們提供了RxLifeScope庫來處理協程開啟、關閉及異常處理,用法如下:

4.1、在
FragemntActivity/Fragment/ViewModel環境下

在該環境下,直接調用rxLifeScope對象的lanuch方法開啟協程即可,如下:

<code>rxLifeScope.lanuch({
     
    val student = RxHttp.postForm(

"/service/..."

) .toClass() .

await

() }, { })/<code>

以上代碼,會在頁面銷燬時,自動關閉協程,同時自動關閉請求,無需擔心內存洩露問題

4.2、非
FragemntActivity/Fragment/ViewModel環境下

該環境下,我們需要手動創建RxLifeScope對象,隨後調用lanuch方法開啟協程

<code>

val

job = RxLifeScope().lanuch({

val

student = RxHttp.postForm(

"/service/..."

) .toClass() .await() }, { }) job.cancel()/<code>

以上代碼,由於未與生命週期綁定,故我們需要在合適的時機,手動關閉協程,協程關閉,請求也會跟著關閉

4.3、監聽協程開啟/結束回調

以上我們在lanuch方法,傳入協程運行回調及異常回調,我們也可以傳入協程開啟及結束回調,如下:

<code>rxLifeScope.launch({                                      
     
    val student = RxHttp.postForm(

"/service/..."

) .toClass() .

await

() }, { }, { }, { }) /<code>

以上回調,均運行在UI線程

5、小結

可以看到,前面文章開頭提到超時/重試問題,就用timeout/retry,延遲就用delay/startDelay,出現異常不想中斷協程的運行,就用
onErrorReturn/onErrorReturnItem或者tryAwait,總之,一切都是那麼的優雅。

RxHttp的優雅遠不止這些,BaseUrl的處理,文件上傳/下載/進度監聽,緩存處理、業務code統一判斷等等,處理的都令人歎為觀止,

更多功能查看以下文章

更多協程用法:
https://juejin.im/editor/posts/5e77604fe51d4527066eb81a

RxHttp非協程用法:
https://juejin.im/post/5ded221a518825125d14a1d4

最後,開源不易,寫文章更不易,如果你覺得不錯RxHttp或給你帶來了幫助,歡迎點贊收藏,以備不時之需,如果可以,再給個star(源碼地址:
https://github.com/liujingxing/okhttp-RxHttp),我將感激不盡,


分享到:


相關文章: