Image for post
Image for post

Coroutines and RxJava — An Asynchronicity Comparison (Part 2): Cancelling Execution

Manuel Vivo
Apr 11, 2018 · 5 min read

Introduction

In this blog series I will compare Kotlin Coroutines and RxJava since they are both trying to solve a common problem in Android development: Asynchronous Programming.

In Part 1 we learned how to perform heavy computation tasks in the background. What if we want to interrupt that computation?

Part 2 is about Cancelling Execution.

What Does Cancel Execution Mean?

We want to be able to cancel the execution of a computation that has been created with RxJava or Coroutines. This computation can be asynchronous or not.

This is important in different use cases in Android development — the most common one might be when the View is going to be destroyed. If that happens, we might want to cancel the ongoing executions such as a network request, a heavy Object initialization, etc.

RxJava

As in Part 1, we’re going to omit the ability of transferring streams of elements. How can we cancel execution with RxJava?

Let’s imagine we create a timer with the interval operator.

When you subscribe to this observable, the timer is going to kick off and it will send an event to the subscriber every second after the subscription.

How Can You Cancel That Timer?

When you subscribe to the timer(calling .subscribe()), it returns a Disposable object.

You can call dispose() on the Disposable object to cancel execution. The Observable will finish emitting items.

That’s it! We have cancelled the asynchronous computation that the Observable had created.

Caveats

If you manually create your own Observable without using any create operator (like interval), you don’t need to handle the cancellation of the computation yourself.

This Observable is not ready to be cancelled. If we want that to happen, we need to check if the emitter is still subscribed before calling it.

If the subscriber is no longer there, we can skip emitting the rest of the items. If we don’t do that, the code will keep running and ignoring the emitter.onNext(i) calls according to the Observable.create source code.

Coroutines

A Coroutine is an instance of a computation itself. Cancelling a Coroutine means stopping the execution of its suspending lambda.

We can cancel execution with the Coroutine Job, which is part of the Coroutine Context.

The Coroutine Job exposes a method to cancel the execution of the Coroutine. As we might expect, that method is called cancel().

For example, the Coroutine Builder launch returns the Job of the Coroutine that it creates.

We can assign that to a value and call cancel.

That was an example of getting the Job from the Coroutine and cancelling it. Can you do it in a different way? Yes, you can also specify the Job of a Coroutine. You can do that in multiple ways.

Some Coroutine Builders (e.g. launch and async) take a named parameter called parent with which you can set the Job for the Coroutine that will be created.

async(CommonPool, parent = parentJob) {
// my suspending block
}
parentJob.cancel()

One of the benefits of this approach is that you can share that parentJob instance with multiple Coroutines, so when you call parentJob.cancel() you are going to cancel the execution of those coroutines that have parentJob as their Job.

This approach is similar to RxJava CompositeDisposable with which you can dispose multiple subscriptions at once.

val deferred1 = async(CommonPool, parent = parentJob) {
// my suspending block
}
val deferred2 = async(CommonPool, parent = parentJob) {
// my suspending block
}
parentJob.cancel() // Cancels both Coroutines

NOTE: You should be careful when sharing Jobs between different Coroutines. When you cancel a Job, you need to reassign it. You cannot a start another Coroutine with that Job, you will have to create a new one.

When you cancel a Job, you need to reassign it.

Another way of doing this is by combining Coroutine Contexts. You can use the plus operator to do this.

launch(parentJob + CommonPool) {
// my suspending block
}
parentJob.cancel()

In this case, the result Coroutine Context of that Coroutine is the combination of parentJob and CommonPool. The threading policy will be defined from CommonPool and the Job value from parentJob.

If you want to learn more about combining contexts you can read this part of the Kotlin Coroutines documentation.

Caveats

Just like RxJava, you have to consider cancellation in Coroutines.

job.cancel()

If we try to execute this code, it will repeat the heavy computation 5 times since the code is not ready to be cancelled.

How can we improve it?

The same way we checked if the subscriber was present in RxJava, we need to check if the Coroutine is active.

job.cancel()

isActive is a an internal variable that can be accessed inside a Coroutine (the coroutineContext is another variable).

Some suspending functions available in the Standard Coroutines library handle cancellation for us. Let’s take a look at delay.

job.cancel()

Delay is a suspending function that can handle the cancellation for us. However, if you use Thread.sleep instead of delay, since it’s blocking the thread and not suspending the coroutine, it won’t cancel it.

job.cancel()

Thread.sleep doesn’t cancel the execution for us. It’s not even a suspending function! That coroutine won’t be cancelled even though we called job.cancel().

Thread.sleep is not something you should use in this instance. If you really really need to, a way of cancelling that coroutine would be checking if it’s active just before and after the thread blocks.

job.cancel()

What’s Coming Next?

The third part of this series will be about transferring stream of elements.

What are the differences between Observable and Channels? Subjects and Broadcast Channels? Don’t miss the third part next week.

More Education

Did you miss Coroutines and RxJava comparison Part 1?

You want to learn more about Kotlin? Check out this article!

Thanks for reading,

Manuel Vicente Vivo

Capital One Tech

The low down on our high tech from the engineering experts…

Manuel Vivo

Written by

Android DevRel @ Google

Capital One Tech

The low down on our high tech from the engineering experts at Capital One. Learn about the solutions, ideas and stories driving our tech transformation.

Manuel Vivo

Written by

Android DevRel @ Google

Capital One Tech

The low down on our high tech from the engineering experts at Capital One. Learn about the solutions, ideas and stories driving our tech transformation.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store