首页 > 解决方案 > 在suspendCancellableCoroutine中取消回调

问题描述

我在 suspendCancellableCoroutine 中包装了一个回调,以将其转换为挂起函数:

suspend fun TextToSpeech.speakAndWait(text: String) : Boolean {
    val uniqueUtteranceId = getUniqueUtteranceId(text)
    speak(text, TextToSpeech.QUEUE_FLUSH, null, uniqueUtteranceId)
    return suspendCancellableCoroutine { continuation ->
        this.setOnUtteranceProgressListener(object : JeLisUtteranceProgressListener() {
            override fun onDone(utteranceId: String?) {
                if(utteranceId == uniqueUtteranceId) {
                    Timber.d("word is read, resuming with the next word")
                    continuation.resume(true)
                }
            }
        })
    }
}

我用片段的生命周期范围协程范围调用这个函数,我假设它在片段被销毁时被取消。但是,LeakCanary 报告说我的片段因为这个监听器而泄漏,我用日志验证了即使在协程被取消后也调用了回调。

所以似乎用suspendCancellableCoroutine而不是suspendCoroutine包装并不足以取消回调。我想我应该主动检查作业是否处于活动状态,但是如何?我尝试coroutineContext.ensureActive()检查coroutineContext.isActive回调内部,但 IDE 给出错误提示“只能在协程主体内调用暂停函数”我还能做些什么来确保在作业被取消时它不会恢复?

标签: androidcallbackkotlin-coroutinescancellation

解决方案


如果您想删除JeLisUtteranceProgressListener无论结果如何(成功、取消或其他错误),您都可以使用经典的 try/finally 块:

suspend fun TextToSpeech.speakAndWait(text: String) : Boolean {
    val uniqueUtteranceId = getUniqueUtteranceId(text)
    speak(text, TextToSpeech.QUEUE_FLUSH, null, uniqueUtteranceId)
    return try { 
        suspendCancellableCoroutine { continuation ->
            this.setOnUtteranceProgressListener(object : JeLisUtteranceProgressListener() {
                override fun onDone(utteranceId: String?) {
                    if(utteranceId == uniqueUtteranceId) {
                        Timber.d("word is read, resuming with the next word")
                        continuation.resume(true)
                    }
                }
        })
    } finally {
        this.setOnUtteranceProgressListener(null)
    }
}

推荐阅读