I'm relatively new to coroutines, so I was wondering how can I solve my small local problem without much restructuring my Android codes.
Here is a simple setup. My ViewModel calls a suspend
function from repository:
// ...ViewModel.kt
fun loadData() {
viewModelScope.launch {
val data = dataRepository.loadData()
}
}
This is quite convenient, since I have a viewModelScope
prepared for me by Android and I call a suspend function from my Repository. I don't care how the repository loads the data, I just suspend till it is returned to me.
My data repository makes several calls using Retrofit
:
//...DataRepository.kt
@MainThread
suspend fun loadData(): ... {
// Retrofit switches the contexts for me, just
// calling `suspend fun getItems()` here.
val items = retrofitApi.getItems()
val itemIDs = items.map { it.id }
// Next, getting overall list of subItems for each item. Again, each call and context
// switch for `suspend fun retrofitApi.getSubItems(itemID)` is handled by Retrofit.
val subItems = itemIDs.fold(mutableListOf()) { result, itemID ->
result.apply {
addAll(retrofitApi.getSubItems(itemID)) // <- sequential :(
}
}
return Pair(items, subItems)
}
As you can see, since loadData()
is a suspend function, all the calls to retrofitApi.getSubItem(itemID)
will be executed sequentially.
However, I would like to execute them in parallel, something like async() / await()
in coroutines would do.
I want to keep the ViewModel
codes untouched - it should not care how the data is loaded, just launches a suspend function from own scope. I also don't want to pass any kind of scopes or other objects to my repository.
How can I do this inside a suspend function? Is the scope somehow implicitly present there? Is calling async()
possible/allowed/good practice?