Search code examples
androidkotlinandroid-roomviewmodelkotlin-coroutines

Kotlin Flow in ViewModel doesn't emit data from Room table sometimes


I'm trying to combine three different flows in my ViewModel to make a list of items that will then be displayed on a RecyclerView in a fragment. I found out that when navigating to the screen, when there is no data in the table yet, the flow for testData1 doesn't emit the data in the table. Happens probably 1/5 of the time. I assume it's a timing issue because it only happens so often, but I don't quite understand why it happens. Also, this only happens when I'm combining flows so maybe I can only have so many flows in one ViewModel?

I added some code to check to see if the data was in the table during setListData() and it's definitely there. I can also see the emit happening but, there is no data coming from room. Any guidance would be greatly appreciated!

Versions I'm using:

Kotlin: 1.4.20-RC
Room: 2.3.0-alpha03

Here is my ViewModel

class DemoViewModel @Inject constructor(
        demoService: DemoService,
        private val demoRepository: DemoRepository
) : ViewModel() {

    private val _testData1 = demoRepository.getData1AsFlow()
    private val _testData2 = demoRepository.getData2AsFlow()
    private val _testData3 = demoRepository.getData3AsFlow()

    override val mainList = combine(_testData1, _testData2, _testData3) { testData1, testData2, testData3 ->
        setListData(testData1, testData2, testData3)
    }.flowOn(Dispatchers.Default)
            .asLiveData()

    init {
        viewModelScope.launch(Dispatchers.IO) {
            demoService.getData()
        }
    }

    private suspend fun setListData(testData1: List<DemoData1>, testData2: List<DemoData2>, testData3: List<DemoData3>): List<CombinedData> {
        // package the three data elements up to one list of rows

        ...
    }

}

And here is my Repository/DAO layer (repeats for each type of data)

@Query("SELECT * FROM demo_data_1_table")
abstract fun getData1AsFlow() : Flow<List<DemoData1>>

Solution

  • I was able to get around this issue by removing flowOn in the combine function. After removing that call, I no longer had the issue.

    I still wanted to run the setListData function on the default dispatcher, so I just changed the context in the setListData instead.

    class DemoViewModel @Inject constructor(
            demoService: DemoService,
            private val demoRepository: DemoRepository
    ) : ViewModel() {
    
        private val _testData1 = demoRepository.getData1AsFlow()
        private val _testData2 = demoRepository.getData2AsFlow()
        private val _testData3 = demoRepository.getData3AsFlow()
    
        override val mainList = combine(_testData1, _testData2, _testData3) { testData1, testData2, testData3 ->
            setListData(testData1, testData2, testData3)
        }.asLiveData()
    
        init {
            viewModelScope.launch(Dispatchers.IO) {
                demoService.getData()
            }
        }
    
        private suspend fun setListData(testData1: List<DemoData1>, testData2: List<DemoData2>, testData3: List<DemoData3>): List<CombinedData> = withContext(Dispatchers.Default) {
            // package the three data elements up to one list of rows
    
            ...
        }
    
    }