https://kotlinlang.org logo
#coroutines
Title
# coroutines
s

streetsofboston

12/05/2018, 2:39 PM
Hello, Maybe I missed it somewhere in the documentation, but I think I can’t find a proper way to suspend a
Job
forever - the only way to complete the
Job
would be through cancellation of its scope. I used the
delay(Long.MAX_VALUE)
to accomplish this, but this seems wrong. Basically, the function below returns a channel that keeps sending location-updates until its enclosing scope gets cancelled.
Copy code
override fun CoroutineScope.publishLocations(): ReceiveChannel<Pair<Double, Double>> =
        Channel<Pair<Double, Double>>(capacity = Channel.CONFLATED).apply {
            val locationListener = locationListener {
                launch { send(it.latitude to it.longitude) }
            }

            launch {
                // locationListener keeps being called each time the phone's
                // location changes, and the locations are sent to `this` Channel over and over again.
                locationManager.requestLocationUpdates(5_000L, 0f, CRITERIA, locationListener, null)

                delay(Long.MAX_VALUE) // suspend forever ...
            }.invokeOnCompletion {
                // .. until the Job (its outer CoroutineScope) is cancelled.

                // Close this Channel
                close(it)
                // And stop the locationManager from sending additional updates.
                locationManager.removeUpdates(locationListener)
            }
        }
Is there a better alternative to creating a
Job
that suspends forever until it’s cancelled?
p

plastiv

12/05/2018, 3:33 PM
You are likely looking into ways of wrapping callback api with coroutines https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#wrapping-callbacks
s

streetsofboston

12/05/2018, 3:40 PM
Thank you @plastiv, But I couldn’t find any info in this about the use-case in the example I posted. My example keeps sending data, over and over again, to a
Channel
until the
Job
is cancelled. My example works, but i’m wondering if this is the correct way of doing this. Using a
suspendCoroutine
, using the
Continuation
it provides, is only for a callback that happens once, not one that gets called over and over again.
d

Dico

12/06/2018, 12:34 AM
you could close the channel from the receiving side, detect the exception that occurs when you attempt to send an item, exit a loop and stop receiving updates
I can't really think of another way to do it, but suspending forever just doesn't seem like the right way to me
also, since it's a CONFLATED channel, you can just invoke
channel.offer
instead of
channel.send
, which is not a suspending function so you don't need to wrap it in a coroutine.
s

streetsofboston

12/06/2018, 1:41 AM
Thank you, @Dico I’ll be looking at the
offer
method. Using a coroutine (launching it within a Scope) is the most convenient way, that I found, to be able to handle a Scope-cancellation (e.g. the Activity/Fragment is destroyed) while the Job is handling callbacks from the GPS, as long as the Job is not cancelled. I don’t know another way of keeping the Job alive while callbacks from the GPS keep coming in; this seems easiest by just suspending indefinitely the Job until it is cancelled (by the enclosing Scope) or by an exception. But I agree… using a looooooong delay or a
suspendForever
smells a bit off 🙂
j

Jonathan

12/06/2018, 9:33 AM
Actually a "suspendForever" may be added in kotlinx.coroutines: https://github.com/Kotlin/kotlinx.coroutines/issues/289
👍🏻 2
s

streetsofboston

12/06/2018, 11:53 AM
@Jonathan That is great! And it means I had at least the right idea to use this pattern fro this type of callbacks :)