简体   繁体   中英

Migrating from RxJava2 to Kotlin Coroutines

I'm trying to migrate my code from RxJava2 to Coroutines. But I'm not sure how to achieve that.

For example, this is my old code to insert a code into the Room Database:

fun insert(note: Note) = Single.fromCallable {
        dao.insert(note)
    }.subscribeIn({ id ->
        note.id = id
        if (note.bitmap != null) update(note)
    }

Note: This code is in an object called DataHelper, which contains all the methods and the Dao object.

This is the Dao Call:

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(note: Note): Long

Trying to replace that code with coroutine calls isn't clear, as I can't call a suspend function from the main thread.

fun insert(note: Note) {
        val id = withContext(Dispatchers.IO) {
            dao.insert(note)
        }
        note.id = id
        if (note.bitmap != null) update(note)
    }

dao.insert() is now a suspend function in the Dao.

Making the insert(Note) function a suspend function means I have to call it with a Dispatcher from any place (Eg, a fragment). Which either means there has to be a Dispatcher in every fragment or activity, or having the whole line of calls suspended.

What is the right way to run background threads with Coroutines?

You can use suspended functions in your Room Dao:

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(note: Note): Long

Whenever you want to access/call this Dao method, you'll need CoroutineScope to do that.

Method 1 Use GlobalScope:

GlobalScope.launch(Dispatchers.IO) {
    dao.insert(note)
    withContext(Dispatchers.Main){
     // main thread calls here, e.g. updating view, showing toast, etc
    }
}

Method 2 Create a CoroutineScope :

val job = SupervisorJob()
val scope = CoroutineScope(Dispatchers.IO + job)

scope.launch {
    dao.insert(note)
    withContext(Dispatchers.Main){
     // main thread calls here, e.g. updating view, showing toast, etc
    }
}

Note: When your instance (eg Activity/Fragment) is destroyed, you can call: job.cancel() to cancel your coroutine jobs.

Method 3 : You can extend your class with CoroutineScope :

class MainActivity : AppCompatActivity(), CoroutineScope {
    override val coroutineContext: CoroutineContext = Dispatchers.IO + SupervisorJob()

    ....

    fun onSomeAction() {
        launch {
            dao.insert(note)
            withContext(Dispatchers.Main) {
                // main thread actions
            }
        }
    }
}

If you're using ViewModel for calling dao methods, then you can use viewModelScope extension:

viewModelScope.launch(Dispatchers.IO) {
    dao.insert(note)
    withContext(Dispatchers.Main){
     // main thread calls here, e.g. updating view, showing toast, etc
    }
}

Unlike other options, viewModelScope will be auto cancelled when onCleared() method of ViewModel is called.

To use this option you need to include this dependency in your app level build.gradle file:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01"

In you example fun insert should be a suspend function:

suspend fun insert(note: Note) {
        withContext(Dispatchers.IO) {
            dao.insert(note)
        }
        //note.id = id
        //if (note.bitmap != null) update(note)
    }

and then from your ViewModel use viewModelScope:

viewModelScope.launch {
            noteRepository.createNote(note)
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM