Moving to 1.3 coroutines - don't quite get it, but tried anyway! Correctly?


#1

I read the blog post by @elizarov on the shift to Structured concurrency and it was a bit… woosh. (which is fine, I’m not the target audience)

I never fully understood why launch(UI) { ... } was good, bad, or even necessary, and to be honest, I don’t want to know - I want things to work without crashing, it is ok if I don’t get how the underlying system handles it.

So I went ahead and accepted the IDE’s suggestions, and ended up with calls like GlobalScope.launch(Dispatchers.Main) { …do stuff… }

… is this good? Do I like GlobalScope? Do I like Dispatchers.Main? (and do they like me back enough not to throw random errors like when I used launch(UI) incorrectly?) Because it is in an Android activity, should I even be using any of that and would a launch { ... } be better?

And if you know the answer to those, I’m betting you know the answer to “is this a sane way to do lazy suspend?

Yours in accepting “accept suggestion” when the IDE says to
-Benjamin


#2

Check out the updated coroutine UI documentation on “Structured concurrency, lifecycle and coroutine parent-child hierarchy”. Your Activity should implement the new CoroutineScope interface. Then you can launch a coroutine on the UI thread with launch { }, and it will automatically be cancelled when the Activity is destroyed.


#3

Oh! huh!
Well, I made my own ScopedAppActivity copying the example, and it seems to work… so… yay?


#4

And today with new releases, it now says I’m doing it all wrong. :confused: I think I should stick with 1.2 until things settle.


#5

I ran into an oddity:
val deferred = (1..maxAttempts).map { attemptNum -> async { ...do stuff
stopped using up all cores, seemed to be constrained to 100% cpu. (which is only 1 of my 8 cores)

Adding in async(Dispatchers.Default) { to the async call started running everything in parallel again, correctly using 700% cpu (7 of 8 cores)


#6

So, did you solve the problem?


#7

Yes, but it felt funny implementing “CoroutineScope” and manually putting in the lines below. I was hoping for more “it just works!” parallel magic. But once I had this, all the sub-classes could use launch {... without issue.

abstract class ScopedActivity : AppCompatActivity(), CoroutineScope {
    private lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        job = Job()
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
}

#8

And in another project, I did not get it to work, and instead ripped out everything Kotlin Sequence/Actor/Coroutine/Channel and went back to plain java threads + Blocking Queue. Which makes me sad because I feel like it would be easier with Kotlin if I knew how to make it work.