使用 Retrofit 和 Coroutines 實(shí)現(xiàn)網(wǎng)絡(luò)請求

Retrofit + kotlin coroutines 使用

如何使用 Retrofitcoroutine 實(shí)現(xiàn)網(wǎng)絡(luò)請求呢? 下面內(nèi)容會做一個簡單的介紹贩疙。

我們使用 ViewModel 處理網(wǎng)絡(luò)請求,使用 LiveData 監(jiān)聽數(shù)據(jù)變化况既。
以下內(nèi)容分為以下幾部分:

  1. 依賴庫版本版本要求
  2. 利用 suspend 關(guān)鍵字定義 API 接口
  3. ViewModel 中使用 coroutines 發(fā)起網(wǎng)絡(luò)請求
  4. ActivityFragment 中通過 observer 觀察 liveData
  5. 總結(jié)
1. 依賴庫版本版本要求:
  1. retrofit 2.6.0 以上
    changeLog
    changeLog 上可以看到这溅,從 2.6.0 以上,retrofit 開始支持 suspend 關(guān)鍵字棒仍,這也是協(xié)程的關(guān)鍵悲靴。

    // 現(xiàn)在可以這么聲明一個網(wǎng)絡(luò)接口
    @GET("users/{id}")
    suspend fun user(@Path("id") id: Long): User
    
  2. kotlin-coroutines-android
    除了依賴 kotlin 外,需要引入?yún)f(xié)程相關(guān)庫

    api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
    api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
    
2. 定義 API 接口

如上面所示莫其,API 接口的樣子為:

interface NewService {
    /**
     * 首頁的 banner 的請求
     */
    @GET("/banner/json")
    suspend fun getBanner(): Response<HomeBanner>

    @GET("/article/list/{page}/json")
    suspend fun getArticleList(@Path("page") page: Int): Response<HomeListResponse>
}

上述接口為 https://www.wanandroid.com/ 的開放 api

實(shí)用 suspend 關(guān)鍵字標(biāo)注方法癞尚,并且返回類型為 Response 包裹的 所需要的數(shù)據(jù)類型.

和正常使用的 retrofit 接口唯一不同的地方是使用了 suspend 關(guān)鍵字。

suspend 是一個標(biāo)志乱陡,告訴該函數(shù)的調(diào)用者浇揩,「這個函數(shù)」是一個耗時的操作,必須放入在協(xié)程中調(diào)用憨颠。

有關(guān)協(xié)程的仔細(xì)解析临燃,我會放在另外一篇文章去寫。

3. 在 ViewModel 中發(fā)起網(wǎng)絡(luò)請求

首先在 Activity 或者 Fragment 中如何獲取到 ViewModel

val firstHomeVM = ViewModelProviders.of(this).get(FirstHomeViewModel::class.java)
// 當(dāng)需要獲取數(shù)據(jù)時烙心,如下
firstHomeVM.getBannerData()

FirstHomeViewModel 中 請求的網(wǎng)絡(luò)訪問:
getBannerData() 為例:

/**
 * 獲取首頁 banner 信息
 */
fun getBannerData() {
    // 第一處
    viewModelScope.launch(IO) {
        // 第二處
        val result = getBannerUseCase.getWanAndroidBanner()
        if (result is Result.Success) {
            // 第三處
            withContext(Main) {
                emitUIBanner(result.data)
            }
        } else if (result is Result.Error) {
            withContext(Main) {
                emitUIEmptyBanner()
            }
        }
    }
}

如代碼所示, 我們標(biāo)注了三處地方:「第一處」,「第二處」乏沸,「第三處」, 下面分析一下這幾處的代碼實(shí)現(xiàn)淫茵。

3.1 第一處:viewModelScope
首先,代碼中 viewModelScope 來自 liftcycle-viewmodel-ktx-2.2.0 蹬跃,是 ViewModel 的一個擴(kuò)展屬性匙瘪,當(dāng) ViewModelcleared 時铆铆,viewModelScope 會被 cancel 掉。

kotlin 中丹喻,協(xié)程總是運(yùn)行在以 CoroutineContext 類型為代表的上下文中薄货。

在這個里面, launch() 方法有三個參數(shù):

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    ...
}

前兩個參數(shù)都有對應(yīng)的默認(rèn)值碍论,這里我們傳入的是一個閉包 block谅猾,在閉包中,是我們真正需要執(zhí)行的邏輯鳍悠。

  • CoroutineContext: 如果我們不傳遞税娜,默認(rèn)會運(yùn)行在 viewModelScope 所在的線程 -> main 線程, 在這里我們指定為 IO 線程。

3.2 第二處:getBannerUseCase.getWanAndroidBanner()

這里是獲取網(wǎng)絡(luò)數(shù)據(jù)的地方藏研。
因為我們標(biāo)明了 IO敬矩, 那么協(xié)成會幫我們切到切到子線程中執(zhí)行 getWanAndroidBanner()

并且由于是掛起函數(shù) suspend 蠢挡, 會在這個位置掛起當(dāng)前線程弧岳,切到其他線程「主線程」做其他的事。

當(dāng)結(jié)果 result 回來時业踏,會繼續(xù)執(zhí)行下面的 if (result ...) 代碼禽炬。
getWanAndroidBanner() 的本質(zhì)上是進(jìn)行了網(wǎng)絡(luò)訪問, 代碼可簡單寫為

/**
 * 獲取 banner 信息
 */
suspend fun getWanAndroidBanner(): Result<List<HomeBanner.BannerItemData>> {
    var result
    val bannerResult = wanService.getBanner()
    // 成功時
    if (bannerResult.isSuccessful && bannerResult.body() != null) {
        val body = bannerResult.body()
        
        result =  Result.Success(body)
    } else {
        result = Result.Error(Exception("獲取 banner 失敗 error code ${bannerResult.code()} error body is ${bannerResult.errorBody()} "))
    }
    // 再次處理 result
    ...
    Log.i("zc_test", "hahahha current thread is ${Thread.currentThread()}")
    return result
}

上述代碼中的 Result 是我本地寫的一個堡称,統(tǒng)一對返回的結(jié)果進(jìn)行了包裝瞎抛。

3.3 第三處:切換現(xiàn)場到主線程
withContext()kotlinx-coroutines-core 中提供的一個 suspend 掛起函數(shù),便于我們切換線程却紧。

Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result.
public suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
)

在這里會掛起桐臊,并且執(zhí)行特定的閉包「我們傳入的 block」, 直到 block 運(yùn)行完。

在我們的代碼中:

withContext(Main) {
    ...
    emitUIBanner(result.data)
}

// emitUIBanner(result.data) 的代碼實(shí)現(xiàn)
/**
 *  在 ui  現(xiàn)場中調(diào)用晓殊,刷新 banner
 */
UiThread
private fun emitUIBanner(banners: List<HomeBanner.BannerItemData>) {
    bannerUILD.value = banners
}

我們切換該閉包在 Main 主線程中執(zhí)行断凶,里面的代碼,本質(zhì)上是刷新 UI 的邏輯巫俺。
利用 LiveDatasetValue() 方法认烁,通知外部可刷新 UI 了。

上面我們看到了 MainIO介汹, 是我們分別指定的協(xié)程調(diào)用器却嗡,協(xié)程會在指定的調(diào)度器上運(yùn)行,并且會自動切換線程嘹承。

4. 在 ActivityFragment 中通過 observer 觀察 liveData

上面我們已經(jīng)獲取到了數(shù)據(jù)窗价,并且通過 LiveData.setvalue() 設(shè)置了數(shù)據(jù),那么我們得需要接收方拿到該數(shù)據(jù)后才會觸動刷新操作叹卷。

ActivityFragment 中通過 observer 觀察 liveData.

代碼如下:

// banner 成功的 監(jiān)聽
val bannerLDObserver = Observer<List<HomeBanner.BannerItemData>> {
    // 刷新 UI
    bannerList.addAll(it)
    bannerDataList.addAll(it)
    startSwitchJob()
    bannerAdapter.notifyDataSetChanged()
    // 刷新 UI
    home_swipe_refresh.isRefreshing = false
}
firstHomeVM.bannerUILD.observe(this, bannerLDObserver)

通過上面四個步驟撼港,我們實(shí)際上完成了「獲取數(shù)據(jù)」和「更新 UI」的操作坪它。
也是我們本次想要分享的內(nèi)容,如果利用 kotlin coroutinesretrofit 實(shí)現(xiàn)網(wǎng)絡(luò)請求帝牡。

5. 總結(jié)

上述簡單的說明了往毡,利用 kotlin coroutines 如何實(shí)現(xiàn)一些網(wǎng)絡(luò)請求,對于耗時的一些操作我們都可以使用 kotlin coroutines 去實(shí)現(xiàn)靶溜。

至于為什么使用 kotlin coroutines 這里就不再討論开瞭。

2019.12.5 by chendroid

水一下~
希望盡快詳細(xì)寫一篇有關(guān)協(xié)程的文章。

balabala

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墨技,一起剝皮案震驚了整個濱河市惩阶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扣汪,老刑警劉巖断楷,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異崭别,居然都是意外死亡冬筒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門茅主,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舞痰,“玉大人,你說我怎么就攤上這事诀姚∠炫#” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵赫段,是天一觀的道長呀打。 經(jīng)常有香客問我,道長糯笙,這世上最難降的妖魔是什么贬丛? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮给涕,結(jié)果婚禮上豺憔,老公的妹妹穿的比我還像新娘。我一直安慰自己够庙,他們只是感情好恭应,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耘眨,像睡著了一般暮屡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毅桃,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天褒纲,我揣著相機(jī)與錄音,去河邊找鬼钥飞。 笑死莺掠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的读宙。 我是一名探鬼主播彻秆,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼结闸!你這毒婦竟也來了唇兑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤桦锄,失蹤者是張志新(化名)和其女友劉穎扎附,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體结耀,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡留夜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了图甜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碍粥。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖黑毅,靈堂內(nèi)的尸體忽然破棺而出嚼摩,到底是詐尸還是另有隱情,我是刑警寧澤矿瘦,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布枕面,位于F島的核電站,受9級特大地震影響匪凡,放射性物質(zhì)發(fā)生泄漏膊畴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一病游、第九天 我趴在偏房一處隱蔽的房頂上張望唇跨。 院中可真熱鬧,春花似錦衬衬、人聲如沸买猖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玉控。三九已至,卻和暖如春狮惜,著一層夾襖步出監(jiān)牢的瞬間高诺,已是汗流浹背碌识。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虱而,地道東北人筏餐。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像牡拇,于是被迫代替她去往敵國和親魁瞪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Why 簡化異步代碼的編寫惠呼。 執(zhí)行嚴(yán)格主線程安全確保你的代碼永遠(yuǎn)不會意外阻塞主線程导俘,并增強(qiáng)了代碼的可讀性。 提升代...
    熹哥閱讀 560評論 0 3
  • 譯者前言 這是協(xié)程官方文檔中早期的一篇關(guān)于響應(yīng)式流與協(xié)程關(guān)系的一篇指南剔蹋,2019 年下半年后旅薄,由于協(xié)程推出了自己的...
    _Preacher_閱讀 2,250評論 0 0
  • 我是在深入學(xué)習(xí) kotlin 時第一次看到協(xié)程,作為傳統(tǒng)線程模型的進(jìn)化版滩租,雖說協(xié)程這個概念幾十年前就有了赋秀,但是協(xié)程...
    前行的烏龜閱讀 99,881評論 32 182
  • 開篇語 最近筆者考完了所有的考試,只需要做課程設(shè)計就可以等著滾回家睡大覺了律想。所以要開始著手傷心團(tuán)隊的項目事宜了猎莲。最...
    張照博閱讀 949評論 0 21
  • 1.進(jìn)程 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序。每個進(jìn)程之間是獨(dú)立的技即,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    西揚(yáng)閱讀 208評論 0 0