3

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 (E.g, 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?

1
  • If you don't want to make CoroutineContext of every activity/fragment then use GlobalScope to call your function in insert method, although it's not best practice to do. Commented Jul 19, 2019 at 12:41

2 Answers 2

4

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 (e.g. 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"
Sign up to request clarification or add additional context in comments.

1 Comment

In Method 3 I believe you can simply do CoroutineScope by CoroutineScope(Dispatchers.IO) and avoid having the override val coroutineContext.... See kotlinlang.org/docs/reference/coroutines/… and also github.com/Kotlin/kotlinx.coroutines/blob/master/ui/… for more. Note that if you want to cancel execution in onDestroy() you need to manually call cancel().
2

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)
        }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.