Reputation: 1920
Lets assume that i have a list of Dtos, i want to loop throught them and set some values and then insert/update them to my Room database. So from my ViewModel i call the repository class, run the loop inside there and then i call dao.insertItems(list).
fun updateItems(itemList: List<ItemDto>) {
val readDate = DateUtils.getCurrentDateUTCtoString()
???SCOPE???.launch(Dispatchers.IO) {
for (item in itemList)
item.readDate = readDate
itemDao.updateItems(itemList)
}
}
The question is what kind of courtineScope do i have to use inside the Repository class. Do i have to create a repositoryScope with Dispatchers.Main..? Perhaps a GlobalScope..?
Upvotes: 19
Views: 10012
Reputation: 4700
You should default to using suspend functions in your repository that you can call from the view model using the viewModelScope
.
class MyRepository(
private val itemDao: ItemDao,
private val ioDispatcher: CoroutineDispatcher,
) {
suspend fun updateItems(itemList: List<ItemDto>) {
withContext(ioDispatcher) {
val readDate = DateUtils.getCurrentDateUTCtoString()
for (item in itemList) {
item.readDate = readDate
itemDao.updateItems(itemList)
}
}
}
}
class MyViewModel(private val repository: MyRepository) : ViewModel() {
fun updateItems() {
viewModelScope.launch {
repository.updateItems()
}
}
}
If you need an operation that shouldn't be cancelled by the viewmodel, then create a scope in the application and inject it into your repository.
class MyRepository(
private val itemDao: ItemDao,
private val externalScope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher,
) {
suspend fun updateItems(itemList: List<ItemDto>) {
withContext(ioDispatcher) {
val readDate = DateUtils.getCurrentDateUTCtoString()
externalScope.launch(externalScope.coroutineContext) {
for (item in itemList) {
item.readDate = readDate
itemDao.updateItems(itemList)
}
}
}
}
}
class MyApplication : Application() {
// No need to cancel this scope as it'll be torn down with the process
val applicationScope = CoroutineScope(SupervisorJob() + otherConfig)
}
Do not use NonCancellable unless you want to suspend cleanup code. Using NonCancellable is very risky as you lose control of the execution of the coroutine. It can lead to subtle and very hard to debug bugs.
If you're using Hilt, inject the scope like this: https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528
Upvotes: 0
Reputation: 2870
Create your method inside repository as suspend fun
and use withContext(Dispatchers.IO)
e.g.
suspend fun updateItems() = withContext(Dispatchers.IO) {
//you work...
}
Upvotes: 4
Reputation: 29280
You should write your repository APIs as suspend
functions instead, like so
suspend fun updateItems(itemList: List<ItemDto>) = withContext(Dispatchers.IO) {
val readDate = DateUtils.getCurrentDateUTCtoString()
for (item in itemList)
item.readDate = readDate
itemDao.updateItems(itemList)
}
Edit: if you need this to run even after the viewmodel is destroyed, launch it with NonCancellable
,
suspend fun updateItems(itemList: List<ItemDto>) = withContext(Dispatchers.IO + NonCancellable) {
val readDate = DateUtils.getCurrentDateUTCtoString()
for (item in itemList)
item.readDate = readDate
itemDao.updateItems(itemList)
}
Upvotes: 16