I'm creating a simple app using Room. I have created a DAO interface:
interface ItemDao {
@Insert(onConflict = IGNORE)
fun addItem(item: Item)
}
And a repository interface:
interface ItemRepository {
fun addItem(item: Item)
}
Here is the implementation of the function:
class ItemRepositoryImpl(
private val itemDao: ItemDao
) : ItemRepository {
override fun addItem(item: Item) = itemDao.addItem(item)
}
And inside the ViewModel class I launch a coroutine using viewModelScope:
class ItemViewModel @Inject constructor(
private val repo: ItemRepository
) : ViewModel() {
fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
repo.addItem(item)
}
}
When I call addItem
from the UI, the item is successfully added to Room. Now the question is, is it mandatory to set the addItem function inside the ItemDao interface as a suspend function? If yes, why? I'm really confused.
Edit:
Here is how I create an instance of my db:
fun provideItemDb(
@ApplicationContext
context : Context
) = Room.databaseBuilder(
context,
ItemDb::class.java,
"item_table"
).build()
Suspend functions are not required, but if you don't mark the functions as suspend
, then they block, so they are not safe to call on the main thread.
If you are using coroutines, it is safe to call a blocking IO function, but only if your current CoroutineContext is not using Dispatchers.Main
. You are currently getting away with not using suspend
functions because you are running your coroutine with Dispatchers.IO
, so the function is called off of the main thread.
If you are using coroutines, for code simplicity you should always mark them suspend
, so the functions are easier and more versatile to use. Imagine if, for example, you wanted to also update a LiveData with the row ID of the newly added item. (You can do this by returning a Long in the DAO function.)
If you use suspend
in your DAO, your function could be:
fun addItem(item: Item) = viewModelScope.launch {
_latestRowLiveData.value = repo.addItem(item)
}
If you didn't use suspend
in your DAO, your function would have to be one of these more convoluted implementations:
fun addItem(item: Item) = viewModelScope.launch {
_latestRowLiveData.value = withContext(Dispatchers.IO) { repo.addItem(item) }
}
// or
fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
_latestRowLiveData.postValue(repo.addItem(item))
}
// or
fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
val rowId = repo.addItem(item)
withContext(Dispatchers.Main) {
_latestRowLiveData.value = rowId
}
}
Now imagine a function that wants to do a series of various actions. The non-suspend version of your DAO would lead to very messy-looking (and therefore bug-prone) coroutines in your ViewModel.