1
votes

I'm using RoomDao with kotlin coroutines and Flow. What I'm trying to do is collect one Training with all its Exercises with all Repetitions per Exercise. Exercises and Repetitions are Flows, cuz this values can be changed and I want to observe them. The problem is that when I updating exercises, getTrainingExerciseLinksBy doesn't triggers, and I don't know, why. Here is my code in UseCase:

suspend fun getTrainingWithExercisesAndRepetitionsBy(trainingId: Long): Flow<UiTrainingWithExercisesAndRepetitions> {
    /// This method returns Flow<List<TrainingExerciseLink>>
    return trainingExerciseLinksRepository.getTrainingExerciseLinksBy(trainingId).flatMapConcat { trainingExerciseLinks ->
        trainingExerciseLinks.map { trainingExerciseLink ->
            /// This method returns Flow<List<ExerciseRepetition>>
            repetitionsRepository.getExerciseRepetitionsBy(trainingExerciseLink.id).map { repetitions ->
                /// do some other selects for collecting data about exercise in one training
            }.flowOn(Dispatchers.IO)
        }.zipFlows()
    }.flowOn(Dispatchers.IO)
}

In my ViewModel I'm observing this method like this:

viewModelScope.launch {
    useCase.getTrainingWithExercisesAndRepetitionsBy(trainingId)
        .distinctUntilChanged()
        .collect {
            _exercisesListLiveData.value = it.exercises
            _trainingListLiveData.value = it.trainingData
        }
}

What is wrong with this code?

UPD: In my DAO I'm using Flows for subscribing on database's updates, like this:

@Dao
abstract class TrainingExerciseLinkDao {

    @Query("select * from TrainingExerciseLink where trainingId = :trainingId")
    abstract fun getTrainingExerciseLinksBy(trainingId: Long): Flow<List<TrainingExerciseLink>>

}

and ExerciseRepetitionsDao:

@Dao
abstract class ExerciseRepetitionDao {

    @Query("select * from ExerciseRepetitionEntity where trainingExerciseId = :trainingExerciseId")
    abstract fun getExerciseRepetitionsBy(trainingExerciseId: Long): Flow<List<ExerciseRepetitionEntity>>

}
2

2 Answers

1
votes

You are using it wrong , as when database updates your getTrainingWithExercisesAndRepetitionsBy does not know, to get over this issue use flows in your dao like this example as Room supports Flow then

viewModelScope.launch {
    viewModel.yourFunctionThatGetsDataFromRepository(trainingId)
        .distinctUntilChanged()
        .collect {
            _exercisesListLiveData.value = it.exercises
            _trainingListLiveData.value = it.trainingData
        }
}

and if more you can refer this example

1
votes

Actually I found the answer, so maybe somebody will jump in the same gap and this thread will be helpful. The problem in my code was that I used flatMapConcat. This operator waits emits from original Flow and from flatMapped Flow at one time, so in this case it will trigger callback. To fix this, flatMapLatest should be used. You can read more about difference between this operators here.

So my code now looks like this:

suspend fun getTrainingWithExercisesAndRepetitionsBy(trainingId: Long): Flow<UiTrainingWithExercisesAndRepetitions> {
    /// This method returns Flow<List<TrainingExerciseLink>>
    /// Here is main change: flatMapConcat -> flatMapLatest
    return trainingExerciseLinksRepository.getTrainingExerciseLinksBy(trainingId).flatMapLatest { trainingExerciseLinks ->
        trainingExerciseLinks.map { trainingExerciseLink ->
            /// This method returns Flow<List<ExerciseRepetition>>
            repetitionsRepository.getExerciseRepetitionsBy(trainingExerciseLink.id).map { repetitions ->
                /// do some other selects for collecting data about exercise in one training
            }.flowOn(Dispatchers.IO)
        }.zipFlows()
    }.flowOn(Dispatchers.IO)
}