Bios90
Bios90

Reputation: 811

How to cancel coroutine with blocking code

I'm trying to run some long running task in coroutine. Sometimes i need to cancel this coroutine, but after job.cancel() it still continues to work.

Here is some code demonstration similar to my real code:

class TestActivity : AppCompatActivity()
{
    val job = Job()

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch(Dispatchers.IO + job, block =
        {
            for (i in 0..100)
            {
                Log.e("TestActivity", "Time passed = ${i * 100}")
                Thread.sleep(100)
            }

            Log.e("TestActivity", "Code after cycle")
        })

        Handler().postDelayed(
            {
                Log.e("TestActivity", "**** Trying to cancel job ****")
                job.cancel()
            }, 3000)
    }
}

In my logcat i get logs after cancelling job.

...
TestActivity: Time passed = 2300
TestActivity: Time passed = 2400
TestActivity: **** Trying to cancel job ****
TestActivity: Time passed = 2500
TestActivity: Time passed = 2600
...
TestActivity: Code after cycle

What is the proper way to cancel such tasks?

Upvotes: 1

Views: 535

Answers (1)

Glenn Sandoval
Glenn Sandoval

Reputation: 3745

You have to make your coroutines cooperative with cancellation. If you make use of any suspending function from kotlinx.coroutines then it's already cooperative with cancellation. For example instead of Thread.sleep use delay:

...
for (i in 0..100)
{
    Log.e("TestActivity", "Time passed = ${i * 100}")
    delay(100)
}
...

When you have a heavy task and you're not calling a suspending function from kotlinx.coroutines, you can check its isActive property when necessary which will be false if the coroutine is cancelled:

...
for (i in 0..100)
{
    if(!isActive)
        break;
    ...
}
...

Upvotes: 4

Related Questions