hi everyone! I have a question: If I have a `lifecycleScope` and I want to run some job that either ...
j
hi everyone! I have a question: If I have a
lifecycleScope
and I want to run some job that either runs after a certain time or event (so can get cancelled) what is the best way of doing so? For example, using:
Copy code
val job = supervisorJob()
val myScope = lifecycleScope + job

// Somewhere else
job.cancelChildren()
Is not working out, because the
myScope
stays alive after the `lifecycleScope`is cancelled Using
Copy code
var job: Job? = null
val theWork = lifecycleScope.launch {
    // delay, do the work
}

// Somewhere else
job?.cancel()
This would work, but then I always have to keep track of the job. Is it possible to have a lifecycle respecting scope on which I can cancel its children when necessary?
Copy code
class SupervisorScope(other: CoroutineScope): CoroutineScope {
	
	private val job = SupervisorJob()
	
	private val scope = other + job

	override val coroutineContext: CoroutineContext
		get() = scope.coroutineContext
	
	init {
		other.launch {
			try {
				awaitCancellation()
			}
			finally {
				scope.cancel()
			}
			
		}
	}

	fun cancelChildren() {
		job.cancelChildren()
	}
	
}
Would this be a bad idea?
Could use it as
Copy code
val supervisorScope = SupervisorScope(lifecycleScope)
supervisorScope.launch {
    //...
}
supervisorScope.cancelChildren()
d
I hardly can understand what you want to achieve. Do you want your coroutine to be cancelled once lifecycle reach some point, like onStop() or onDestroy()? This can be done with lifecycle extension methods such as launchWhenCreated/launchWhenStarted
It may require to add some additional dependencies to your project tho
j
no I want to launch work, what might be restarted/cancelled based on user input
on a timed basis
and also the work should be cancelled after the lifecycle died
d
lifecycleScope is enough in your case. Not sure about your "user input". All you need is to hold a reference to a Job that returned by your lifecycleScope.launchWhen
So you can cancel it manually
One more thing to note here, if you are using lifecycleScope in a fragment, you want to start a coroutine using viewLifecycle Owner.lifecycleScope.launchWhen
By calling viewLifecycleOwner you bind your coroutine to a fragment view lifecycle
j
I know that I can reference the job, I was just wondering if there would be a way to not having to reference it (It would safe me a whopping 1 line, perhaps 2 lines of code 😄 )
d
Ah, I see. What is your "user input" then?
What should trigger cancellation?
j
For example: I display the ETA of a vehicle in minutes. I launch a job, that at the correct time this eta counts down. If I get an update on the vehicle location and the ETA, I should cancel the old job for updating the UI and launch a new one to show the remaining minutes and count them down
so for example: ETA is 6:20. Then at that time I would display "Vehicle arrives in 7 minutes". Then after 20 seconds, I update the text "Vehicle arrives in 6 minutes" and after that I wait 1 minute to display the text "Vehicle arrives in 5 minutes".
d
Flows are pretty good for this case
j
yeah I was just thinking about it
stateFlow holding ETA combined with a collectLatest and a while loop
d
I see it smth like this. First: etaFlow.flatMapLatest { update stuff and eta, emit to etaFlow }.collect() // this will update eta and location Second: etaFlow.flatMapLatest { parse ETA to your UI string } // this will take last eta and emit human readable strings. this flow you may collect on your activity/fragment and set emitted string to your label
I've done similar task few weeks ago, so I could help you to make it work with more specific code, but I might need know more about your code structure. Feel free to PM me if you need help with this