文章目錄
- 線程 API 轉換成掛起函數:suspendCoroutine
- 支持取消的 suspendCoroutine:suspendCancellableCoroutine
- 總結
線程 API 轉換成掛起函數:suspendCoroutine
在實際項目中即使已經使用協程了,可是要完全避免跟傳統的線程 API 交互并不容易,大型項目一般都會有比較多的老代碼或外部庫沒有用協程,使用的還是回調的寫法。那么就很有必要知道怎么讓協程和線程 API 的回調交互。
協程有一個專用的函數 suspendCoroutine,它是一個掛起函數,在它里面調用傳統的回調式函數,就能把回調式的函數轉換成掛起函數:
lifecycleScope.launch {val contributors = callbackToSuspend()showContributors(contributors)
}private suspend fun callbackToSuspend() = suspendCoroutine {gitHub.contributorsCall("square", "retrofit").enqueue(object : Callback<List<Contributor>> {override fun onResponse(call: Call<List<Contributor>>, response: Response<List<Contributor>> {// 將結果返回it.resume(response.body()!!)}override fun onFailure(call: Call<List<Contributor>>) {// 發生異常時讓 suspendCoroutine 立即結束并拋出這個異常it.resumeWithException(t)}})}
}
使用 suspendCoroutine 包裹的回調式代碼需要調用 suspendCoroutine 提供的 continuation.resume 和 continuation.resumeWithException 分別處理正常返回結果和異常的情況。
支持取消的 suspendCoroutine:suspendCancellableCoroutine
協程還提供了一個類似的函數 suspendCancellableCoroutine,和 suspendCoroutine 的區別是它支持取消。
private suspend fun callbackToCancellableSuspend() = suspendCancellableCoroutine {it.invokeOnCancellation {// 協程被取消,處理協程被取消時要做的一些收尾工作清理現場}gitHub.contributorsCall("square", "retrofit").enqueue(object : Callback<List<Contributor>> {override fun onResponse(call: Call<List<Contributor>>, response: Response<List<Contributor>> {// 將結果返回it.resume(response.body()!!)}override fun onFailure(call: Call<List<Contributor>>) {// 發生異常時讓 suspendCoroutine 立即結束并拋出這個異常it.resumeWithException(t)}})}
}
使用 suspendCancellableCoroutine 還可以注冊取消的回調,使用 cancellableContinuation.invokeOnCancellation 處理協程被取消時的收尾清理工作。
我們用一個例子來說明 suspendCoroutine 和 suspendCancellableCoroutine 的區別:
val job = lifecycleScope.launch {// 假設 callbackToSuspend 會在延時 2s 后繼續執行try {val contributors = callbackToSuspend()// val contributors = callbackToCancellableSuspend()showContributors(contributors)} catch (e: Exception) {textView.text = e.message}
}
lifecycleScope.launch {delay(200)job.cancel() // 200ms 后取消協程
}
假設 callbackToSuspend 函數是使用 suspendCoroutine 包起來的回調代碼,會在 2s 后返回結果;協程 200ms 后被取消了,但是里面的代碼是不配合的,因為協程的取消本身就是一個狀態標記,2s 后還是會繼續執行代碼。
而如果用 suspendCancellableCoroutine 在 200ms 后會正常取消協程,會在 try-catch 拋出 CancellableException 異常,不會在繼續執行后續代碼。
我們一般在項目中都使用能支持取消的 suspendCancellableCoroutine,除非特殊需求需要啟動后協程取消了也得繼續執行才用 suspendCoroutine。
總結
-
將線程 API 的回調式代碼用 suspendCoroutine 或 suspendCancellableCoroutine 包住,就能實現將回調式代碼轉換為掛起函數在協程執行,需要調用提供的 continuation.resume 和 continuation.resumeWithException 分別處理正常返回結果和異常的情況
-
suspendCancellableCoroutine 和 suspendCoroutine 的區別是它支持取消和注冊協程取消回調;我們一般在項目中都使用能支持取消的 suspendCancellableCoroutine,除非特殊需求需要啟動后協程取消了也得繼續執行才用 suspendCoroutine