
Cancelling Kotlin Coroutines
The latest project my team and I are working on involves manipulating a large amount of data. As this has the potential to block the UI thread and give a poor user experience, we took this as an opportunity to get more familiar with Kotlin Coroutines and use them to handle the heap of data in the background.
The data processing is triggered by UI events in which a user can interact with continuously. So, if the user taps around multiple times, it is important for us to handle the cancellation of any currently running coroutine jobs before starting up a new one.
The points below are not earth shattering revelations. But when you’re jumping into something new and making your way through the documentation combined with various online articles, you can miss some things. So, I thought I would share a few points around the cancellation of coroutines that were not obvious to us at the time.
- Cancellation Cooperative
Just like Android’sAsyncTask
, coroutines are cancellation cooperative. When callingcancel
on a coroutine job, theisActive
flag is set to false, but the job will continue to run. This means that if you have a long running loop inside of a coroutine but do not check for theisActive
flag to exit out on, the loop will continue until complete. In this example, a good place to check for theisActive
flag might be after each iteration of the loop. - CancellationException
If there is a suspending function currently running inside of a coroutine andcancel
is called on the job, once the suspending function has finished it will throw aCancellationException
and deem the job complete.
For example, if there were 3suspend
functions in a coroutine andcancel
was called on the job during the secondsuspend
function, the third would not get invoked.
It is important to note here that point 1 above regarding theisActive
flag still applies inside asuspend
function, i.e, the second function would continue to run until completion if there were no checks forisActive
.
When giving examples of cancellation in coroutines, most articles use thedelay
function inside of a loop to illustrate when and where the code stops running. I found this slightly misleading as the only reason the loop is exiting at that exact point is becausedelay
is of course asuspend
function, and hence throwing theCancellationException
. It is not clear here that without having thedelay
function inside of the loop, the loop would continue to run. - invokeOnCompletion
If you have appliedinvokeOnCompletion
onto a coroutine job, this will still get triggered even if the reason for the job completing is due to it being cancelled. However, becausecancel
has been called, the job flagisCancelled
will now be set to true. You are then able to use the flag to check for any tidy ups inside ofinvokeOnCompletion
or choose not to progress with any further code.
These aspects of coroutines are certainly covered in the documentation but sometimes it takes a while to piece it all together. Hopefully you already have the cancellation of your jobs under control, but if not, perhaps the above can help tidy up any loose ends.