'How do you call a suspend function inside a SAM?

I'm trying to create a Flow that needs to emit values from a callback but I can't call the emit function since the SAM is a normal function

Here's the class with the SAM from a library that I can't really modify it the way I need it to be.

class ValueClass {

    fun registerListener(listener: Listener) {
        ...
    }    

    interface Listener {
        fun onNewValue(): String
    }
}

And here's my take on creating the Flow object

class MyClass(private val valueClass: ValueClass) {
    fun listenToValue = flow<String> {
        valueClass.registerListener { value ->
            emit(value) // Suspension functions can only be called on coroutine body
        }
    }
}

I guess it would've been simple if I could change the ValueClass but in this case, I can't. I've been wrapping my head around this and trying to look for implementations.

At least from what I know so far, one solution would be to use GlobalScope like this

class MyClass(private val valueClass: ValueClass) {
    fun listenToValue = flow<String> {
        valueClass.registerListener { value ->
            GlobalScope.launch {
                emit(value)
            }
        }
    }
}

Now, this works but I don't want to use GlobalScope since I'll be using viewModelScope to tie it to my app's lifecycle.

Is there any way to work around this?

Thanks in advance. Any help would be greatly appreciated!



Solution 1:[1]

You can use callbackFlow to create a Flow from the callback. It will look something like:

fun listenToValue(): Flow<String> = callbackFlow {
    valueClass.registerListener { value ->
        trySend(value)
        channel.close() // close channel if no more values are expected
    }
    awaitClose { /*unregister listener*/ }
}

Or if only one value is expected from the callback, you can use suspendCoroutine or suspendCancellableCoroutine. It this case listenToValue() function must be suspend and later called from a coroutine(e.g. someScope.launch):

suspend fun listenToValue(): String = suspendCoroutine { continuation ->
    valueClass.registerListener { value ->
        continuation.resumeWith(value)
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1