Leave a rating/review
Hello! Welcome to the eighth episode of the Kotlin Coroutines Fundamentals course.
This time you’ll learn more about the coroutine jobs.
You may recall the coroutine Jobs from the previous episodes. For example, a Job is the result of the coroutine builder invocation.
A Job is a handle to the coroutine execution. Using a Job you can check the status of the coroutine. Whether it’s running, cancelled, or finished.
You can also cancel it, add a completion callback or wait for a coroutine to finish.
Every coroutine has its job. There are jobs acting as parents for other jobs.
A parent waits for all its children to finish before it finishes itself.
A Job can be in the several states. By default it begins in the Active state and starts running immediately along with its coroutine.
Optionally you may use the lazy coroutines by adjusting the start parameter of the coroutine builder. They won’t start executing until explicit order and they will be in the New state until then.
If the coroutine finishes successfully, its job will be in the Completed state.
If it fails or get cancelled it will go through the Cancelling state and then end up in the Cancelled state.
Cancelling includes the time between the cancellation request and the actual end of the coroutine. A parent will wait in a Cancelling state for all its children to finish their cancellations.
You can check the current state of the job by reading the isActive, isCompleted and isCancelled properties of the Job.
Let’s see the jobs in action! Open the JobsScreen file in Android Studio.
In the first button click handler you can see the code starting a simple coroutine. It has an explicitly specified parent job in the launch builder as a context element.
There is also logging the job state changes to the LogCat.
Run the app.
Go to the Jobs screen.
Click the first button.
Open the LogCat.
Look at the LogCat. The parent job is active all the time. Since the very beginning, even before the child coroutine started, to the very end.
Now, go to the second button click handler.
Add a job cancellation to it by writing:
parentJob.cancel()
Run the app.
Go to the Jobs screen.
Click the first button and then immediately click the second button.
Open the LogCat.
As you can see, there is no last log saying the coroutine finished. And the parent job is in the Cancelled state.
Close the LogCat.
Click the first button again.
Go back to the LogCat.
This time the coroutine does not start at all. The cancelled parent job is not accepting new children.
You may notice there are no separate states for failed and cancelled jobs. That’s because the cancellation is just a special case of a failure.
They differ only by the cause of the failure. In case of cancellation it’s the CancellationException.
This type of exception is thrown from the suspension points when the job executing the coroutine turns out to be cancelled.
Let’s check that out!
Go back to Android Studio.
Wrap the delay() call with the try-catch block and log the exception to the LogCat:
try {
delay(2.seconds)
} catch (e: Exception) {
Log.d("JobsScreen", "Delay failed", e)
}
Run the app and go to the Jobs screen.
Click the buttons in the same order.
Open the LogCat.
The execution reached the catch block. It means that the delay() function threw an exception.
But, the message about coroutine finished is also there! It is usually not what you want. The execution should not reach the code after cancellation.
Let’s fix that! Add rethrowing CancellationExceptions from the catch block.
if (e is CancellationException) {
throw e
}
Run the app and click the buttons in the same order.
Go back to the LogCat.
Now, a message about the coroutine finished is not there.
Additionally, the CancellationException hasn’t reached the uncaught exception handler. This class of exceptions are not considered as failures.
Remember to rethrow any CancellationExceptions caught inside the coroutines. Otherwise the
coroutines may keep executing after cancellation. That’s an easy way to memory leaks.
There is a special kind of job called the SupervisorJob. You may recall it from the previous episodes.
The only difference between the Job and the SupervisorJob is the behaviour when one of the children fails.
In case of regular Job, the failure will cancel the parent and its other siblings.
But, the children of SupervisorJob are independent from each other. A failure of one of them won’t affect the parent and the other siblings.
That’s it for this episode. I hope you got the idea of the coroutine jobs.
In the next episode you’ll learn about the async/await pattern in coroutines.