BG_TeMe
BG_TeMe

Reputation: 55

Kotlin Coroutines Flow with Room run again whenever device back to active

I'm facing a very strange behavior when using Kotlin Coroutines Flow, Room and Live Data. Whenever I turn off my device for about 5-10s and turn it back on, the Coroutines Flow re-run all over again without any trigger. I'm not sure if it's a feature provided by Google or not. My code is as below.

MainActivity

wordViewModel = ViewModelProvider(this).get(WordViewModel::class.java)

wordViewModel.allWords.observe(this, Observer { words ->

    words?.let { adapter.setWords(it) }

})

WordViewModel

class WordViewModel(private val repository: WordRepository) : ViewModel() {

    val allWords = repository.allWords.onEach { Log.v("WordViewModel", "Flow trigger again") }.asLiveData()

}

WordRepository

class WordRepository(private val wordDao: WordDao) {

    val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()

    suspend fun insert(word: Word) {
        wordDao.insert(word)
    }
}

WordDao

@Dao
interface WordDao {

    @Query("SELECT * from word_table ORDER BY word ASC")
    fun getAlphabetizedWords(): Flow<List<Word>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(word: Word)
}

Word

@Entity(tableName = "word_table")
data class Word(@PrimaryKey @ColumnInfo(name = "word") val word: String)

The LogCat V/WordViewModel: Flow trigger again will be printed out again when I turn off my device for about 5-10s and turn it back on. Also, the device I'm using to test is Sony XZ2 run on Android 10.

If anyone know why this is happening, please help me understand. Thanks and sorry for my english.

EDIT

As the answer from @Alex Krafts, this feature is provided by Google. But because of my Kotlin Coroutines Flow will be combine with a network request. Therefore, I don't want it run again when device become active. I wrote a custom asLiveData() extension function for this case as follow.

LiveDataExtension

fun <T> Flow<T>.asLiveData(scope: CoroutineScope): LiveData<T> {
    val liveData = MutableLiveData<T>()
    scope.launch {
        collect {
            liveData.value = it
        }
    }
    return liveData
}

WordViewModel

class WordViewModel(private val repository: WordRepository) : ViewModel() {

    val allWords = repository.allWords.onEach { Log.v("WordViewModel", "Flow trigger again") }.asLiveData(viewModelScope)

}

Upvotes: 5

Views: 3330

Answers (1)

Alex Krafts
Alex Krafts

Reputation: 525

This feature is indeed provided by Google. You are providing MainActivity(this) as LifecycleOwner in

wordViewModel.allWords.observe(this, Observer { words ->

so when you turn off device screen, activity (due to its own lifecycle) stops observing allWords and observe it again when you turn device screen back on. So that's where your logs come from.

From the documentation

After a cancellation, if the LiveData becomes active again, the upstream flow collection will be re-executed.

Upvotes: 2

Related Questions