uli
06/09/2022, 4:32 PMfun <V : Any> SharedPreferences.asFlow(
key: String,
defaultValue: V?,
retrieve: SharedPreferences.(String, V?) -> V?,
): Flow<Optional<V>> {
return callbackFlow {
val prefsListener = SharedPreferences
.OnSharedPreferenceChangeListener { sharedPreferences, k ->
if (k == key) {
val value = sharedPreferences.retrieve(key, defaultValue)
trySendBlocking(value.asOptional())
}
}
registerOnSharedPreferenceChangeListener(prefsListener)
awaitClose {
unregisterOnSharedPreferenceChangeListener(prefsListener)
}
}
.onStart {
emit(Optional.ofNullable(retrieve(key, defaultValue)))
}
.distinctUntilChanged()
As it looks, this code is racy, if shared preferences are changed after onStart
emits, but collecting of the callbackFlow has not yet started.
Anyone has any hints on an elegant solution to this? Like a way to start collecting the callback flow immediately, but still injecting a first element?Trevor Stone
06/09/2022, 5:16 PMawaitClose
Trevor Stone
06/09/2022, 5:16 PMsend
technicallyuli
06/09/2022, 10:27 PMTrevor Stone
06/09/2022, 10:52 PMfun <V : Any> SharedPreferences.asFlow(
key: String,
defaultValue: V?,
retrieve: SharedPreferences.(String, V?) -> V?,
): Flow<V?> {
return callbackFlow {
coroutineScope {
val firstValueMutex = Mutex(true)
val prefsListener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, k ->
if (k == key) {
val value = sharedPreferences.retrieve(key, defaultValue)
launch(start = CoroutineStart.UNDISPATCHED) { firstValueMutex.withLock { send(value) } }
}
}
registerOnSharedPreferenceChangeListener(prefsListener)
send(retrieve(key, defaultValue))
firstValueMutex.unlock()
awaitClose { unregisterOnSharedPreferenceChangeListener(prefsListener) }
}
}
.distinctUntilChanged()
}
uli
06/09/2022, 10:56 PMJoffrey
06/09/2022, 10:56 PMuli
06/09/2022, 10:58 PMuli
06/09/2022, 11:00 PMuli
06/09/2022, 11:02 PMmyanmarking
06/13/2022, 9:36 AMJoffrey
06/13/2022, 9:37 AMcallbackFlow
literally gives a channel to send elements to. Doesn't mean there can't be a race between different parallel senders to the channelmyanmarking
06/13/2022, 9:38 AMJoffrey
06/13/2022, 9:40 AMmyanmarking
06/13/2022, 9:42 AMmyanmarking
06/13/2022, 9:44 AMJoffrey
06/13/2022, 9:50 AMmyanmarking
06/13/2022, 10:00 AMJoffrey
06/13/2022, 10:01 AMmyanmarking
06/13/2022, 10:02 AMJoffrey
06/13/2022, 10:03 AMJoffrey
06/13/2022, 10:04 AMJoffrey
06/13/2022, 10:06 AMmyanmarking
06/13/2022, 10:06 AMJoffrey
06/13/2022, 10:08 AMmyanmarking
06/13/2022, 10:12 AMmyanmarking
06/13/2022, 10:12 AMJoffrey
06/13/2022, 10:12 AMif-else
has the racemyanmarking
06/13/2022, 10:13 AMJoffrey
06/13/2022, 10:14 AMmyanmarking
06/13/2022, 10:15 AMuli
06/13/2022, 10:15 AMuli
06/27/2022, 3:54 PMonSubscription
instead of onStart might do the trick. The documentation looks promising. I’ll think about it.