Migrating experimental Kotlin Coroutines to stable version

Patrick Steiger
3 min readDec 20, 2018

--

Kotlin Coroutines, even though already quite stable as in bug-free and ready to use in production code, were deemed experimental, which meant its API could change at any time. And so they did: Kotlin 1.3 was released, coroutines were promoted to stable status, and the API changed slightly, so if you’ve developed on Kotlin 1.2 using experimental coroutines versions pre-1.0, as I did in all of my apps, you’ll need to make some adjustments.

Let’s go through some of the main changes.

Remove ‘experimental’ from imports

This is quite obvious: as coroutines lost its experimental status, the package names changed. You’ll need to adjust the imports, from:

import kotlinx.coroutines.experimental.*

To:

import kotlinx.coroutines.*

Instead of launching in a global scope, local scope is now default

While in experimental status, when you launched a new coroutine it was launched in a global scope and it could outlive the very component it was launched in, as described in this issue.

This behavior was changed: coroutines now launch in a local scope per default, which means it is bound to the scope lifetime: if the component dies, so does the coroutine.

To keep the old behavior in the new API, you need to change from:

launch { doSomething() }

To:

GlobalScope.launch { doSomething() }

However, you really should think about what the appropriate lifetime and scope of each coroutine you launch should be. For instance, if a coroutine changes UI elements of the component it was launched in, it makes no sense for it to be in GlobalScope, and it could crash if the component dies while the coroutine is alive: its lifetime should be the same as (or less than) the UI elements it touches.

CommonPool, UI and other dispatchers have new names

To launch a coroutine in a shared pool of threads that cannot touch UI nor do IO, you once did:

launch(CommonPool) { doSomething() }

Instead of CommonPool, you now must use Dispatchers.Default:

GlobalScope.launch(Dispatchers.Default) { doSomething() }

(Note I’m already adjusting the scope with GlobalScope to keep the same behavior, as explained in the above section)

Or, as you could already do with CommonPool, you can omit the dispatcher parameter completely:

GlobalScope.launch { doSomething() }

Instead of using the UI dispatcher to allow UI operations in the coroutines, you must use Dispatchers.Main. So, the old

launch(UI) { doSomething() }

Becomes:

GlobalScope.launch(Dispatchers.Main) { doSomething() }

The IO dispatcher is now named Dispatchers.IO.

Changes also apply to Actors

Kotlin actors are neat — they empower coroutines with sequential processing.

Its API followed all the API changes described above. But more so, it seems currently complex actors API is obsolete and its future is unknown, as described in this issue. So after adjusting your actors, you might benefit from annotating them (and every other functions that use them) with:

@ObsoleteCoroutinesApi

And sometimes with

@ExperimentalCoroutinesApi

As hinted by Android Studio.

--

--

Patrick Steiger

Professional Android Developer. Android Dev Specialist @ Inter. Indie developer on free time. Passionate about programming. https://github.com/psteiger