Migrating to Android Kotlin 1.3 Coroutines

December 28, 2018

Edit module build.gradle.

dependencies {
    // https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md
    def kotlin_version = '1.3.11'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-android
    // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0'
    // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.27.0-eap13'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0'    
}

Edit gradle.properties to remove kotlin.coroutines=enable.

# kotlin.coroutines=enable

Change import from

import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch

to

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope

Replace code

launch(UI) {

}

launch(CommonPool) {

}

to

// Main/UI Thread
GlobalScope.launch(Dispatchers.Main) {

}

// Background Thread
GlobalScope.launch(Dispatchers.Default) { // or Dispatchers.IO

}

CoroutineScope should be implemented on entities with well-defined lifecycle that are responsible for launching children coroutines. Example of such entity on Android is Activity.

class MyActivity : AppCompatActivity(), CoroutineScope {
    lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        job = Job()
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically
    }

    /*
     * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
     * in this method throws an exception, then all nested coroutines are cancelled.
     */
    fun loadDataFromUI() = launch { // <- extension on current activity, launched in the main thread
           val ioData = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
               // blocking I/O operation
           }
           // do something else concurrently with I/O
           val data = ioData.await() // wait for result of I/O
           draw(data) // can draw in the main thread
        }
    }
}    

NOTE: The above code is copied from here.

NOTE: For ViewModel CoroutineScope, cancel the job at onCleared.

If you are using retrofit2 kotlin coroutine adapter, you will bump into the following error.

java.lang.NoClassDefFoundError: Failed resolution of: Lkotlinx/coroutines/experimental/Deferred;
    at com.jakewharton.retrofit2.adapter.kotlin.coroutines.experimental.CoroutineCallAdapterFactory.get(CoroutineCallAdapterFactory.kt:59)

Replace the dependencies

// implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.