Is this correct and the most concise way of having...
# coroutines
s
Is this correct and the most concise way of having cleanup in the implement CoroutineScope pattern?
Copy code
class Activity : CoroutineScope {
    var job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job

    init {
        val workJob = launch(start = CoroutineStart.LAZY) {
            // Doing stuff
            delay(5000)
        }
        launch(start = CoroutineStart.ATOMIC) {
            try {
                workJob.join()
            } finally {
                println("Cleaning up")
            }
        }
    }

    fun destroy() {
        job.cancel()
    }
}
g
Actually this should be enough:
Copy code
class Activity : CoroutineScope {
   override val coroutineContext = Dispatchers.UI + Job()


  override fun onDestroy() {
      coroutineContext.cancel()
  } 
}
Not sure what code in
init
block should show, doesn’t make a lot of sense for me with all those ATOMIC and additional lazy launch But maybe I don’t see some important usecase
s
I need to perform an action once the job is done.. either by cancellation or completion.
Preferably within the scope of Job so
job.join()
will return after cleanup
g
Why not just use
invokeOnCompletion
?
s
Cant launch new suspendable functions
g
inside of invokeOnCompletion?
s
yes
g
I see
in this case you probably need to coroutines yeah
Also curious about clean approach for this But can you close resources just using onDestroy? Because if your function that cleanups resources is suspendable it also will be cancelled Maybe more clear and stable solution would be just runBlocking in onDestroy or even invokeOnCompletion, otherwise you in trap of async closing operations I of course don’t know everything about your case, so hard to tell will it be better or not
s
The ATOMIC ensures the cleanup operation will run even if the parent is cancelling
g
yes, but what about suspend functions that you call there
you said that you need suspend function and you cannot use invokeOnComplete
s
Good question. I would assume they inherit that status so they cannot be cancelled either
g
as I understand ATOMIC will guarantee only that such coroutine will be started, but if you call any suspend function such function will be just cancelled (I may be wrong, I just thought that it works like that)
I don’t think that atomic make coroutine non cancellable
it’s just about way how you start coroutine, atomic cannot be cancelled before actual start
But again, it’s just my understanding
You can use NonCancellable context for some cases, but not sure that it wat you whant here]
s
A quick test seems to confirm that any
launch
will just be cancelled. Setting the NonCancellable context gives the appropriate behaviour.
g
Why you just cannot wrap your resource with try/finally in your original “worker” launch, you just shouldn’t acquire resource outside of try, as I understand this should work
s
I can call additional suspend methods which I think is enough in my case albeit a bit fragile to rely on
Yeah that would be simpler. I wonder why that got extracted in the first place 😉
g
Yeah, try to use:
Copy code
launch {
            try {
                // Doing stuff
               delay(5000)
            } finally {
                println("Cleaning up")
            }
        }
But again, depends on what is “cleaning up”, it cannot be cancellable suspend function, only blocking or something with NonCancellable context
d
What about launching the cleanup coroutine in global scope?
Or just without the same parent?
k
You can use
withContext(NonCancellable) { ... }
in finally I believe But yea, Andrey already hit on all the points to consider