Reputation: 139
I have an app based on MVVM, There are 5 Dao interfaces:
@Dao
interface DataDaoOne {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(classA: ClassA) : Long
@Query("SELECT * FROM class_a")
fun getData() : LiveData<List<ClassA>>
}
Similarly, the second Dao, and five more.
@Dao
interface DataDaoTwo {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(classB: ClassB) : Long
@Query("SELECT * FROM class_a")
fun getData() : LiveData<List<ClassB>>
}
I access the DAOs using a repository, what I want to do is, create a function that calls all the getData() from the five DAOs and stores the values in a variable. It will wait until the data from all tables are fetched then create another data class that will hold all data fetched from the tables and return it.
E.g:
data class AllData(
val classA: List<ClassA>,
val classB: List<ClassB>,
)
getAllData() : AllData{
val dataA = repository.getClassAData()
val dataB = repository.getClassBData()
return AllData(dataA, dataB)
}
Now I can make an API call and pass this AllData class object to it.
// Fetching the data and storing it in a variable
val allData = getAllData()
// Passing the all data to API calling function.
sendDataToServer(allData)
The problem I am facing is calling the DAO functions from the repository and await for them to fetch the data for all tables, how can be solve this issue?
Upvotes: 0
Views: 77
Reputation: 93834
The LiveData can most easily be combined by converting to flows first. Here, we use asFlow()
to convert them to Flows and asLiveData()
to convert them back. That means there is a lot going on under the hood. You might prefer to define your DAO functions to simply return Flows instead of LiveData, and use the resulting Flow from combining instead of converting it to LiveData.
fun getAllData(): LiveData<AllData> = combine(
repository.getClassAData().asFlow(),
repository.getClassBData().asFlow()
) { a, b -> AllData(a, b) }
.asLiveData()
It sounds like you want to fetch the latest value of all data one time when called. So to do that, you need to to await the first value reported by the LiveData or Flow. To do this with a LiveData, you need to observe
it. With a Flow, you can call first()
on it in a suspend function, like this:
suspend fun getAllData(): AllData = combine(
repository.getClassAData().asFlow(),
repository.getClassBData().asFlow()
) { a, b -> AllData(a, b) }
.first()
This is what I think you would have to do if you don't use Flows:
fun getAllData(): LiveData<AllData> = MediatorLiveData().apply {
var dataA: List<ClassA>? = null
var dataB: List<ClassB>? = null
fun emitIfReady() {
dataA ?: return
dataB ?: return
value = AllData(dataA!!, dataB!!)
}
addSource(repository.getClassAData()) {
dataA = it
emitIfReady()
}
addSource(repository.getClassBData()) {
dataB = it
emitIfReady()
}
}
Upvotes: 0
Reputation: 2513
Well, I would prefer using Flows
now but being relevant to the question you should try using MediatorLiveData
val liveData1: LiveData<Int> = LiveData(1);
val liveData2: LiveData<Int> = LiveData(1);
val liveDataMerger: MediatorLiveData<Int> = MediatorLiveData()
liveDataMerger.addSource(liveData1) {
// onChanged
}
liveDataMerger.addSource(liveData2) {
// onChanged
}
observe
the liveDataMerger
or MediatorLiveData
as you do.
Upvotes: 0