iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0

今天是 codelab 的尾聲了,課程的最後還有談到使用 Retrofit 和 Room 搭配 coroutine 使用,但我們目前還沒用到,暫時先不多談,還記得我們是在寫咖啡廳專案嗎哈哈,失控跑題好幾天拉~~

使用 coroutine 向網路請求資料

Callback 版本

今天要來學習如何切換 coroutine 要運行的執行緒,先上原先使用 callback 的程式碼 :

TitleRepository.kt

fun refreshTitleWithCallbacks(titleRefreshCallback: TitleRefreshCallback) {
   // This request will be run on a background thread 
   BACKGROUND.submit {
       try {
           // Make network request using a blocking call
           val result = network.fetchNextTitle().execute()
           if (result.isSuccessful) {
               // Save it to database
               titleDao.insertTitle(Title(result.body()!!))
               // Inform the caller the refresh is completed
               titleRefreshCallback.onCompleted()
           } else {
               // If it's not successful, inform the callback of the error
               titleRefreshCallback.onError(
                       TitleRefreshError("Unable to refresh title", null))
           }
       } catch (cause: Throwable) {
           // If anything throws an exception, inform the caller
           titleRefreshCallback.onError(
                   TitleRefreshError("Unable to refresh title", cause))
       }
   }
}

來逐步解釋程式碼 :

首先,方法內傳入了從 ViewModel 丟過來的 TitleRefreshCallback,目的是將成功或是失敗的結果通知方法的調用者 :

fun refreshTitleWithCallbacks(titleRefreshCallback: TitleRefreshCallback)

接下來是這句,表示會在背景執行緒中向網路請求資料。

BACKGROUND.submit {}

這邊是使用同步執行向網路請求資料,並取得回傳結果 - result

// Make network request using a blocking call
val result = network.fetchNextTitle().execute()

最後是取得結果的部分,如果成功,會將資料新增至資料庫,並讓 callback 通知調用者任務已完成;失敗也是讓 callback 回傳 TitleRefreshError()通知調用者。

if (result.isSuccessful) {
    // Save it to database
    titleDao.insertTitle(Title(result.body()!!))
    // Inform the caller the refresh is completed
    titleRefreshCallback.onCompleted()
} else {
    // If it's not successful, inform the callback of the error
    titleRefreshCallback.onError(
        TitleRefreshError("Unable to refresh title", null))
}

Coroutine 版本

來看下用 coroutine 改寫的結果 :

suspend fun refreshTitle() {

    withContext(Dispatchers.IO) {

        val result = try {

            network.fetchNextTitle().execute()
        }
        catch (cause: Throwable) {
            throw TitleRefreshError("Unable to refresh title", cause)
        }

        if (result.isSuccessful) {

            titleDao.insertTitle(Title(result.body()!!))
        }
        else{
            throw TitleRefreshError("Unable to refresh title", null)
        }
    }
}

記得如果想用 coroutine 執行,就先把函式掛起 suspend :

suspend fun refreshTitle() {}

在來如果要做耗時的任務,程式碼不能運行在主執行緒。因為我們呼叫的 execute() 和 insertTitle()都會阻塞目前正在運行的 coroutine,所以在這邊加上withContext,並且讓 Dispatchers 指定要切換的執行緒為 IO 執行緒 ,這樣就會讓 coroutine 切換到 IO 執行緒去取得網路資料。

withContext(Dispatchers.IO) {}

接下來一樣是用同步執行來向網路請求資料,並且取得回傳結果 - result。還記得昨天說可以使用 try-catch 處理 coroutine 拋出的錯誤嗎? 所以這邊一樣使用 try-catch 處理,請求失敗就會拋出 Throwable通知調用者。

val result = try {

    network.fetchNextTitle().execute()
}
catch (cause: Throwable) {
    throw TitleRefreshError("Unable to refresh title", cause)
}

最後,若取得的 result 成功的話,就一樣將資料儲存到資料庫,失敗就拋出 TitleRefreshError

if (result.isSuccessful) {

    titleDao.insertTitle(Title(result.body()!!))
}
else{
    throw TitleRefreshError("Unable to refresh title", null)
}

這三天的 Kotlin Coroutine Codelab 讓筆者對於 coroutne 的基本用法比較熟悉了,裡面講的概念比較淺所以查超多資料的,邊上班邊吸收邊發文真的是齁…..

明天就繼續回到我們的找咖啡專案吧!!相信經過這幾天的洗滌應該能實作出來吧!

今日推推

賽程已經過了三分之一拉,來點嗨的~
Yes


上一篇
Day9 實作 Google Codelab Coroutine - 2
下一篇
Day11 二戰 Coroutine ! 使用 OKHttp 串接全台咖啡廳資料的 API-5
系列文
喝咖啡要30天?一起用 Kotlin 打造尋找好喝咖啡的 App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言