-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Pause a job #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Not sure, but I have idea like this: class MyFragment : Fragment() {
...
private val parentJob = Job() // cancel on destroy
private var stoppedJob: Job? = null // active on stop
...
fun onStart() {
super.onStart()
stoppedJob?.cancel()
}
fun onStop() {
super.onStop()
stoppedJob = Job()
}
fun onDestroy() {
super.onDestroy()
parentJob.cancel()
}
private fun showSomething(id: Long) = launch(UI + parentJob) {
val result = asyncSomething(id).await()
stoppedJob?.join()
updateUiBasedOnSomething(result)
}
...
} |
What you exactly want to happen while it is paused? What kind of asynchronous activities you are concerned about? If you've sent some network network request to a server, then it does not make much sense "to pause" it, because there is nothing you are can really do about it -- it is already is flight and is being processed by the server. However, if you doing some UI animations with coroutines or something like that, then pausing them indeed makes sense. In the latter case, I'd suggest to implement a dedicated |
Yes, it's what I meant, there's no problem with the HTTP response, but I want to delay displaying/toasting/animating something based on that response until the fragment/activity started (in case it is stopped). |
Thank you for this thread, I am looking for something like that. asyncOperation.run(args)
.thenPromise(result -> whenActivityRunning.then(() -> result))
.thenVoid(result -> updateUiOrSmthElse(result)); As I undestand, current suggestion is implement a dedicated |
I tried to implement simple pausable dispatcher: class PausableDispatcher(private val handler: Handler): CoroutineDispatcher() {
private val queue: Queue<Runnable> = LinkedList()
private var isPaused: Boolean = false
@Synchronized override fun dispatch(context: CoroutineContext, block: Runnable) {
println("dispatch")
if (isPaused) {
queue.add(block)
} else {
handler.post(block)
}
}
@Synchronized fun pause() {
println("pause")
isPaused = true
}
@Synchronized fun resume() {
println("resume")
isPaused = false
runQueue()
}
private fun runQueue() {
queue.iterator().let {
while (it.hasNext()) {
val block = it.next()
it.remove()
handler.post(block)
}
}
}
} usage: class MainActivity : Activity() {
private val dispatcher = PausableDispatcher(Handler(Looper.getMainLooper()))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button1)
button.setOnClickListener {
launch(dispatcher) {
suspendFunc()
delay(1000)
suspendFunc()
delay(1000)
suspendFunc()
}
}
}
override fun onPause() {
super.onPause()
dispatcher.pause()
}
override fun onResume() {
super.onResume()
dispatcher.resume()
}
override fun onDestroy() {
super.onDestroy()
dispatcher.cancel()
}
} If I "minimize" my application after first suspendFunc call, next call pushes to queue. I am not sure about "dispatcher.cancel", and about implementation details, but it seems to work. Any suggestions? |
I'm closing this issue in favor of #258. Having thought about the issue I don't think that solving "pausing" problem on a dispatcher level is a right way to do it. It is better to explicitly write code that suspends coroutine until the desired state is reached. |
I found one use case where is pausable dispatcher very handy. I will try to explain as a simplified model. Let's say we have some global notification display. We have to display only one at the time. There could be many requests at the time. Also, the user is able to pause notifications at any time. The code could be as simple as that: private val notifications = Channel<Notification>(UNLIMITED)
suspend fun enqueueNotification(notification: Notification) {
notifications.send(notification)
}
suspend fun pause() { pauseable.pause() }
suspend fun resume() { pauseable.resume() }
private fun launchDispacher() {
withContext(pauseable) {
for (notification in notifications) {
val view = prepareView(notification)
showInAnimation(view) //suspension point.
delay(5000) //suspension point
showOutAnimation(view) //suspension point.
}
}
} A user has to be able to pause here. Dont ask why :) I am ok to pause dispacher at any suspension point. |
@neworld |
Because of this: showInAnimation(view) //suspension point.
delay(5000) //suspension point
showOutAnimation(view) //suspension point. Sometimes new job should start from begining, sometimes new job need to close old notification first. And there is very simplified version. Real one has more suspension points. |
I'm trying to understand your real use case, your last code can be rewritten: try {
showInAnimation(view) //suspension point.
delay(5000) //suspension point
} finally {
withContext(NonCancellable) {
showOutAnimation(view) //suspension point.
}
} alternatively you can use a |
Actually, it can't be rewrite in this way, because if the user pauses notification while one is shown, it should be visible as long as the user doesn't resume. for (notification in notifications) {
val view = prepareView(notification)
pauseable.withLock { showInAnimation(view) }
delay(5000) //suspension point
pauseable.withLock { showOutAnimation(view) }
} |
now we can use |
Really? Is it paused everytime the lifecycle pauses, continued when the lifecycle resumes, paused again when the lifecycle pauses again? |
I have checked and it's working as I expected. You can check it by yourself. |
A little bit late :) I have tested and used it., it is working for my case. Thanks a lot @morder this what I am searching for. |
In case of Android, we can cancel a job and its children in
onDestroy
. Is there a way to pause it (e.g. inonStop
) and resume it (e.g. inonStart
)? Or any plan to implement it?The text was updated successfully, but these errors were encountered: