I have two fragments in my app. In first fragment, I can see, that data is collected from database, unofrtunately, after navigating by Navigation Components
to the second fragment, it is not, and I don't know why.
DAO
@Query("SELECT * from base_currency")
fun getBaseCurrency(): Flow<BaseCurrencyModel>
Repository
val baseCurrency: Flow<BaseCurrencyModel> =
currencyDAO.getBaseCurrency().shareIn(
scope, SharingStarted.WhileSubscribed(5000L)
)
ViewModel frag 1
private val _baseCurrencyState: MutableSharedFlow<DatabaseState> = MutableSharedFlow(replay = 1)
val baseCurrency: SharedFlow<DatabaseState> get() = _baseCurrencyState
fun getBaseCurrency() {
viewModelScope.launch {
databaseRepository.baseCurrency
.catch { _baseCurrencyState.emit(DatabaseState.Error(it.cause)) }
.collect { currency ->
_baseCurrencyState.emit(DatabaseState.Success(currency.baseCurr))
}
}
ViewModel frag 2
private val _baseCurrencyState: MutableSharedFlow<DatabaseState> = MutableSharedFlow(replay = 1)
val baseCurrency: SharedFlow<DatabaseState> get() = _baseCurrencyState
fun getBaseCurrency() {
viewModelScope.launch {
databaseRepository.baseCurrency
.catch { _baseCurrencyState.emit(DatabaseState.Error(it.cause)) }
.collect { currency ->
_baseCurrencyState.emit(DatabaseState.Success(currency.baseCurr))
}
}
}
Fragment 1
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mViewModel.getBaseCurrency()
mViewModel.baseCurrency.collect { baseCurrency ->
when (baseCurrency) {
is DatabaseState.Success<*> -> {
mBinding.latestBase.text = String.format(getString(R.string.formatted_base_currency, baseCurrency.data))
// TODO
}
is DatabaseState.Error<*> -> {
Log.i(TAG, "onCreateView: ERROR $baseCurrency")}
}
}
}
}
Fragment 2
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mViewModel.getBaseCurrency()
mViewModel.baseCurrency.collect { baseCurrency ->
when (baseCurrency) {
is DatabaseState.Success<*> -> {
Log.i(TAG, "onCreateView: ${baseCurrency.data}")
}
is DatabaseState.Error<*> -> {
Log.i(TAG, "onCreateView: ERROR $baseCurrency")}
}
}
}
}
Your SharedFlow in the repository doesn't have any replay
, and it uses WhileSubscribed(5000L)
, so if the second fragment is opened within five seconds of the first one going off screen, then the source flow is not restarted when the shared flow is collected in the second ViewModel, and it receives no initial value.
The solution is to add replay = 1
to the shareIn
call in your repository.
By the way, your code in both ViewModels could be significantly simplified by using shareIn
. It is really ugly that they have a backing MutableSharedFlow for no use but to collect another flow into it. And it's even uglier that the collection is done in a function that has to be manually called by an outside class, rather than just doing it in init
. And it breaks function naming convention to name that function get...()
.
val baseCurrency: SharedFlow<DatabaseState> = databaseRepository.baseCurrency
.map { DatabaseState.Success(it.baseCurr) }
.catch { emit(DatabaseState.Error(it.cause)) }
.shareIn(viewMdoelScope, SharingStarted.WhileSubscribed(5000L), replay = 1)