Takshak Rajput
Takshak Rajput

Reputation: 139

How to combine & wait for multiple getData() DAO calls in Android

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

Answers (2)

Tenfour04
Tenfour04

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

Dr. Sa.M.
Dr. Sa.M.

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

Related Questions