Hey guys, I have a list `returnedExpertiseFields` ...
# android
o
Hey guys, I have a list
returnedExpertiseFields
inside a ViewModel. I want to initialize this list using a query to Room.
returnedExpertiseFields
gets returned before it is initialized with values from Room, as in the code below:
Copy code
fun getExpertiseFieldsById(expertiseFieldsIds: IntArray) : List<ExpertiseField>{
    var returnedExpertiseFields = listOf<ExpertiseField>()

    viewModelScope.launch {
        returnedExpertiseFields = mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(expertiseFieldsIds)
    }

    return returnedExpertiseFields
}
returnedExpertiseFields
will return an empty list of
ExpertiseField
. What can I do to make sure I always wait to Room query to be returned? I thought calling launch is synchronous? (If you need my repository code I will post it, I just thought it might not be necessary) Also, If any of you have any tips for debugging when using coroutines I will be glad to hear. I initially tought my Room query is broken. After long debugging I realized I am jumping between breakpoints rather them skipping them.
o
If I understand your problem correctly then your method should look something like this.
Copy code
fun getExpertiseFieldsById(expertiseFieldsIds: IntArray) : List<ExpertiseField> = withContext(<http://Dispatches.IO|Dispatches.IO>) {
mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(expertiseFieldsIds)
}
Then you would call this method like so.
Copy code
fun someMethod() {
viewModelScope.lauch {
val returnedExpertiseFields = getExpertiseFieldsById(ids)
// do something with the fields
}
}
b
what do you think about using
LiveData
in the view model and observing that? something like:
Copy code
val expertiseFields = MutableLiveData<List<ExpertiseField>>()

fun getExpertiseFieldsById(expertiseFieldsIds: IntArray) {
    viewModelScope.launch {
        expertiseFields.postValue(mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(expertiseFieldsIds))
    }
}
and then, from your activity or fragment:
Copy code
// ... inside on onCreate or onViewCreated, for example
yourViewModel.expertiseFields.observe(this, { expertiseFields ->
    // expertiseFields is your list
})
o
Hey @Oleg Siboglov I tried using your code, however I can’t use withContext(IO) inside my function, do you have a suggestion how to restructure that? Isn’t Room queries run on background threads anyway?
@Brian I am thinking about using that, seems like a good Idea, as I just realized that Room queries should be asynchronous https://stackoverflow.com/questions/47112114/android-room-synchronous-queries-return-null
b
yeah, LiveData and ViewModel are pretty well set up to handle situations like this so the fragment or activity can observe when the data is returned from whatever async process might happen in the
viewModelScope
o
Room queries are run on whatever thread that started them. Why can’t you use
<http://Dispatchers.IO|Dispatchers.IO>
?
Meaning you have to start a query on a background thread and not the main thread.
o
Hmm I might did something wrong I am checking @Oleg Siboglov
I combined your two methods into one and I get an error:
Copy code
private fun someMethod(expertiseFieldsIds: IntArray) : List<ExpertiseField>{
    viewModelScope.launch {
        withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
            mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(expertiseFieldsIds)
        }
    }
}
How can I return value from an IO context?
o
In regards to the code you posted
Copy code
viewModelScope.launch {
        returnedExpertiseFields = mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(expertiseFieldsIds)
    }
launch
here is consuming an exception that
Room
would throw warning you about running a query on the main thread.
Copy code
fun someMethod() {
viewModelScope.launch {
val ids = getIds()
val fields = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(ids)
}
//do something with fields
}
}
o
Got it, thank you Oleg @Oleg Siboglov One more thing I didn’t mention. The UI (Some Activity) needs to get these fields Is is possible to pass the fields to the UI without making the UI observervable?
o
So I don’t use
ViewModel
and instead use MVP, so I don’t know all the capabilities of
ViewModel
. However heres an example where
view
is a reference to your UI.
Copy code
fun loadFields() {
CoroutineScope(Dispatchers.Main).launch {
val ids = getIds()
val fields = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
mExpertiseFieldFieldsRepository.getExpertiseFieldsFromDatabaseById(ids)
}
// This coroutine is suspended until the room query completes. When it does complete, this coroutine is resumed and you can update the UI since you are back to using Dispatchers.Main

view.updateAdapterWithFields(fields)
}
}
You can try looking into this code lab to see how coroutines can be used with
Room
and
ViewModel
- https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#2