RxHttp 在v2.0版本中加入對協程的支持,收到了廣大kotlin用戶的喜好,他們也不由感慨,原來協程發請求還能如此優雅,比retrofit強大的不止一點點,然而,這就夠了嗎?遠遠不夠,爲啥,由於還有痛點沒解決,爲此,我也收集幾個目前網絡請求遇到的痛點,以下:java
async
操做符處理異步問題,但用到時,每次還要包裝一次,不能接受等等,其實還有不少小細節的問題,這裏就就不一一列舉了。git
正因有以上問題,因此RxHttp v2.2.0版本就來了,該版本主要改動以下github
asysn
、timeout
、retry
、tryAwait
等等gradle依賴json
dependencies {
//必須
implementation 'com.ljx.rxhttp:rxhttp:2.2.0'
kapt 'com.ljx.rxhttp:rxhttp-compiler:2.2.0' //生成RxHttp類
//如下均爲非必須
//管理協程生命週期,頁面銷燬,關閉請求
implementation 'com.ljx.rxlife:rxlife-coroutine:2.0.0'
//Converter 根據本身需求選擇 RxHttp默認內置了GsonConverter
implementation 'com.ljx.rxhttp:converter-jackson:2.2.0'
implementation 'com.ljx.rxhttp:converter-fastjson:2.2.0'
implementation 'com.ljx.rxhttp:converter-protobuf:2.2.0'
implementation 'com.ljx.rxhttp:converter-simplexml:2.2.0'
}
複製代碼
注:純Java項目,請使用annotationProcessor替代kapt;依賴完,記得rebuild,纔會生成RxHttp類緩存
歡迎加入RxHttp&RxLife交流羣:378530627網絡
相信還有以前沒了解過RxHttp的同窗,這裏貼出RxHttp請求流程圖,記住該圖,你就掌握了RxHttp的精髓,以下: 框架
代碼表示val str = RxHttp.get("/service/...") //第一步,肯定請求方式,能夠選擇postForm、postJson等方法
.toStr() //第二步,確認返回類型,這裏表明返回String類型
.await() //第二步,使用await方法拿到返回值
複製代碼
怎麼樣,是否是很是簡單?異步
該操做符很是強大,不只作到了失敗重試,還作到了週期性失敗重試,即間隔幾秒後重試,來看下完整的方法簽名async
/** * 失敗重試,該方法僅在使用協程時纔有效 * @param times 重試次數, 默認Int.MAX_VALUE 表明不斷重試 * @param period 重試周期, 默認爲0, 單位: milliseconds * @param test 重試條件, 默認爲空,即無條件重試 */
fun retry( times: Int = Int.MAX_VALUE, period: Long = 0, test: ((Throwable) -> Boolean)? = null
)
複製代碼
retry()
方法共有3個參數,分別是重試次數、重試周期、重試條件,都有默認值,3個參數能夠隨意搭配,如:函數
retry() //無條件、不間斷、一直重試
retry(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、一直重試
複製代碼
前兩個參數相信你們一看就能明白,這裏對第3個參數額外說一下,經過第三個參數,咱們能夠拿到Throwable
異常對象,咱們能夠對異常作判斷,若是須要重試,就返回true,不須要就返回false,下面看看具體代碼
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.retry(2, 1000) { //重試2次,每次間隔1s
it is ConnectException //若是是網絡異常就重試
}
.await()
複製代碼
OkHttp提供了全局的讀、寫及鏈接超時,有時咱們也須要爲某個請求設置不一樣的超時時長,此時就能夠用到RxHttp的timeout(Long)
方法,以下:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(3000) //超時時長爲3s
.await()
複製代碼
若是咱們由兩個請求須要並行時,就可使用該操做符,以下:
//同時獲取兩個學生信息
suspend void initData() {
val asyncStudent1 = RxHttp.postForm("/service/...")
.toClass<Student>()
.async() //這裏會返回Deferred<Student>
val asyncStudent2 = RxHttp.postForm("/service/...")
.toClass<Student>()
.async() //這裏會返回Deferred<Student>
//隨後調用await方法獲取對象
val student1 = asyncStudent1.await()
val student2 = asyncStudent2.await()
}
複製代碼
delay
操做符是請求結束後,延遲一段時間返回;而startDelay
操做符則是延遲一段時間後再發送請求,以下:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.delay(1000) //請求回來後,延遲1s返回
.await()
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.startDelay(1000) //延遲1s後再發送請求
.await()
複製代碼
有些狀況,咱們不但願請求出現異常時,直接走異常回調,此時咱們就能夠經過兩個操做符,給出默認的值,以下:
//根據異常給出默認值
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(100) //超時時長爲100毫秒
.onErrorReturn {
//若是時超時異常,就給出默認值,不然,拋出原異常
return@onErrorReturn if (it is TimeoutCancellationException)
Student()
else
throw it
}
.await()
//只要出現異常,就返回默認值
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(100) //超時時長爲100毫秒
.onErrorReturnItem(Student())
.await()
複製代碼
若是你不想在異常時返回默認值,又不想異常是影響程序的執行,tryAwait
就派上用場了,它會在異常出現時,返回null,以下:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(100) //超時時長爲100毫秒
.tryAwait() //這裏返回 Student? 對象,即有可能爲空
複製代碼
map
操做符很好理解,RxJava即協程的Flow都有該操做符,功能都是同樣,用於轉換對象,以下:
val student = RxHttp.postForm("/service/...")
.toStr()
.map { it.length } //String轉Int
.tryAwait() //這裏返回 Student? 對象,即有可能爲空
複製代碼
以上操做符,可隨意搭配使用,但調用順序的不一樣,產生的效果也不同,這裏悄悄告訴你們,以上操做符只會對上游代碼產生影響。
如timeout及retry
:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(50)
.retry(2, 1000) { it is TimeoutCancellationException }
.await()
複製代碼
以上代碼,只要出現超時,就會重試,而且最多重試兩次。
但若是timeout
、retry
互換下位置,就不同了,以下:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.retry(2, 1000) { it is TimeoutCancellationException }
.timeout(50)
.await()
複製代碼
此時,若是50毫秒內請求沒有完成,就會觸發超時異常,而且直接走異常回調,不會重試。爲何會這樣?緣由很簡單,timeout及retry
操做符,僅對上游代碼生效。如retry操做符,下游的異常是捕獲不到的,這就是爲何timeout在retry下,超時時,重試機制沒有觸發的緣由。
在看timeout
和startDelay
操做符
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.startDelay(2000)
.timeout(1000)
.await()
複製代碼
以上代碼,一定會觸發超時異常,由於startDelay,延遲了2000毫秒,而超時時長只有1000毫秒,因此一定觸發超時。 但互換下位置,又不同了,以下:
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.timeout(1000)
.startDelay(2000)
.await()
複製代碼
以上代碼正常狀況下,都能正確拿到返回值,爲何?緣由很簡單,上面說過,操做符只會對上游產生影響,下游的startDelay
延遲,它是無論的,也管不到。
在以上示例中,咱們統一用到await/tryAwait
操做符獲取請求返回值,它們都是suspend
掛起函數,須要在另外一個suspend
掛起函數或者協程中才能被調用,故咱們提供了RxLifeScope庫來處理協程開啓、關閉及異常處理,用法以下:
在FragemntActivity/Fragment/ViewModel環境下
在該環境下,直接調用rxLifeScope
對象的lanuch
方法開啓協程便可,以下:
rxLifeScope.lanuch({
//協程代碼塊,運行在UI線程
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.await()
//可直接更新UI
}, {
//異常回調,這裏能夠拿到Throwable對象
})
複製代碼
以上代碼,會在頁面銷燬時,自動關閉協程,同時自動關閉請求,無需擔憂內存泄露問題
非FragemntActivity/Fragment/ViewModel環境下
該環境下,咱們須要手動建立RxLifeScope
對象,隨後調用lanuch
方法開啓協程
val job = RxLifeScope().lanuch({
//協程代碼塊,運行在UI線程
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.await()
//可直接更新UI
}, {
//異常回調,這裏能夠拿到Throwable對象
})
//在合適的時機關閉協程
job.cancel()
複製代碼
以上代碼,因爲未與生命週期綁定,故咱們須要在合適的時機,手動關閉協程,協程關閉,請求也會跟着關閉
監聽協程開啓/結束回調
以上咱們在lanuch
方法,傳入協程運行回調及異常回調,咱們也能夠傳入協程開啓及結束回調,以下:
rxLifeScope.launch({
//協程代碼塊
val student = RxHttp.postForm("/service/...")
.toClass<Student>()
.await()
//可直接更新UI
}, {
//異常回調,這裏能夠拿到Throwable對象,運行在UI線程
}, {
//開始回調,能夠開啓等待彈窗,運行在UI線程
}, {
//結束回調,能夠銷燬等待彈窗,運行在UI線程
})
複製代碼
以上回調,均運行在UI線程
能夠看到,前面文章開頭提到超時/重試問題,就用timeout/retry
,延遲就用delay/startDelay
,出現異常不想中斷協程的運行,就用onErrorReturn/onErrorReturnItem
或者tryAwait
,總之,一切都是那麼的優雅。
RxHttp的優雅遠不止這些,BaseUrl的處理,文件上傳/下載/進度監聽,緩存處理、業務code統一判斷等等,處理的都使人歎爲觀止,
更多功能查看如下文章
協程用法:RxHttp ,比Retrofit 更優雅的協程體驗
RxJava用法:RxHttp 讓你眼前一亮的Http請求框架
最後,開源不易,寫文章更不易,若是你以爲不錯RxHttp或給你帶來了幫助,歡迎點贊收藏,以備不時之需,若是能夠,再給個star,我將感激涕零,🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏