Just testing Preferences DataStore and found out that the provided Flow output won't emit same value, my setup as followed:
DataStore Util Class:
object DataStore {
private val Context.settings by preferencesDataStore("settings")
suspend fun saveBoolean(context: Context, keyResId: Int, value: Boolean) {
val key = booleanPreferencesKey(context.getString(keyResId))
context.settings.edit {
it[key] = value
}
}
fun getBooleanFlow(context: Context, keyResId: Int, defaultValueResId: Int): Flow<Boolean> {
val key = booleanPreferencesKey(context.getString(keyResId))
val defaultValue = context.resources.getBoolean(defaultValueResId)
return context.settings.data.map {
it[key] ?: defaultValue
}
}
}
ViewModel Class:
class FirstViewModel(application: Application) : AndroidViewModel(application) {
private val uiScope = viewModelScope
val isUpdateAvailable = DataStore.getBooleanFlow(
getApplication(), R.string.is_update_available_key, R.bool.is_update_available_default
)
fun updateIsUpdateAvailable() = uiScope.launch {
DataStore.saveBoolean(getApplication(), R.string.is_update_available_key, true) //<- always set to true
}
}
Fragment Class:
class FirstFragment : Fragment() {
private lateinit var binding: FragmentFirstBinding
private lateinit var viewModel: FirstViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
viewModel = ViewModelProvider(this).get(FirstViewModel::class.java)
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.isUpdateAvailable.collect {
Log.v("xxx", "isUpdateAvailable: $it")
}
}
}
binding.saveButton.setOnClickListener {
viewModel.updateIsUpdateAvailable()
}
return binding.root
}
}
Since I'm saving true each time, and the Log just shows once, which means the Flow doesn't emit same value. Am I correct? Is this intentional behavior?
Right, context.settings.data flow doesn't emit the same value. I haven't found any docs confirming that, but digging into the sources of DataStore library shows that if the current value is equal to the new value, then emitting doesn't happen. The source code of a function that updates the value:
private val downstreamFlow = MutableStateFlow(...)
private suspend fun transformAndWrite(
transform: suspend (t: T) -> T,
callerContext: CoroutineContext
): T {
val curDataAndHash = downstreamFlow.value as Data<T>
curDataAndHash.checkHashCode()
val curData = curDataAndHash.value
val newData = withContext(callerContext) { transform(curData) }
curDataAndHash.checkHashCode()
// here comparison is happening
return if (curData == newData) {
curData
} else {
// if curData and newData are not equal save and emit newData
writeData(newData)
downstreamFlow.value = Data(newData, newData.hashCode())
newData
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With