I have wrapped a callback in suspendCancellableCoroutine to convert it to a suspend function:
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)
}
}
})
}
}
I'm calling this function with the lifecycleScope coroutine scope of the fragment and I was assuming that it was cancelled when fragment is destroyed. However, LeakCanary reported that my fragment was leaking because of this listener and I verified with logs that the callback was called even after the coroutine is cancelled.
So it seems that wrapping with suspendCancellableCoroutine instead of suspendCoroutine does not suffice to cancel the callback. I guess I should actively check whether the job is active, but how? I tried coroutineContext.ensureActive() and checking coroutineContext.isActive inside the callback but IDE gives an error saying that "suspension functions can be called only within coroutine body" What else can I do to ensure that it doesn't resume if the job is cancelled?