Migrating todo-mvp-kotlin to coroutines

Intro
This post shows steps and code comparison of migrating todo-mvp-kotlin sample from callbacks to kotlin coroutines.
Work is currently completed in my fork: mvp-kotlin-coroutines. This is external sample, but link is available in official google repository.
What you need
Before exploring this sample, you should familiarize yourself with the following topics:
- The project README
- The todo-mvp sample
- The todo-mvp-kotlin sample
- Kotlin coroutine documentation
What we want to achieve?
Replace all asynchronous operations with coroutines, which simplify asynchronous programming by providing possibility to write code in direct style (sequentially).
Dependencies
First, we need to introduce two more dependencies.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core: $version"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android: $version"
To ignore the warning that coroutines are experimental, mark them as enabled.
kotlin {
experimental {
coroutines "enable"
}
}
Commit reference: 32b0b1f
Base Implementation
All asynchronous operations are done via TasksRepository
, TasksLocalDataSource
, and TasksRemoteDataSource
which implement TasksDataSource
interface.
To indicate that TasksDataSource
functions may do intensive operation we want to mark all of them as suspend
. Also to get rid of callbacks we will introduce a Result
class.

Result
class is a sealed class which can either be a Success
or Error
. Those functions which earlier accepted a callback as a parameter, now return Result
instead.

AppExecutors
class which earlier defined different executors for different needs now holds different coroutine contexts.

Concrete Implementation
For concrete implementation let’s take a look how function getTask(id)
and related functions have changed.
TasksLocalDataSource
now looks much cleaner without callbacks. We don’t need to deliver result in the main thread, it’s up to the client to decide.

TasksRepository
now doesn’t have nested callbacks, instead, we have nested when
blocks.

Now let’s take a look how client code looks like — TaskDetailPresenter
. Again, we managed to get rid of callbacks, our code is sequential. Besides that, now client decided on which context root coroutine will be executed (in this case it’s main thread).

Commit reference: 19327e7
Tests
To launch coroutines you need to specify a CoroutineContext
. In tests all coroutines are launched via runBlocking
or with EmptyCoroutineContext
.
