今天是 codelab 的尾聲了,課程的最後還有談到使用 Retrofit 和 Room 搭配 coroutine 使用,但我們目前還沒用到,暫時先不多談,還記得我們是在寫咖啡廳專案嗎哈哈,失控跑題好幾天拉~~
今天要來學習如何切換 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
改寫的結果 :
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 的基本用法比較熟悉了,裡面講的概念比較淺所以查超多資料的,邊上班邊吸收邊發文真的是齁…..
明天就繼續回到我們的找咖啡專案吧!!相信經過這幾天的洗滌應該能實作出來吧!