I want to store some preferences using DataStore. But the problem is that my application can have multiple users and therefor needs to store these preferences in separate files. I got a working example using only one user but I'm struggling to support multiple users.
Here is an example of my code:
class DataStorageRepository(private val context: Context, private val userRepository: UserRepository) {
private object PreferencesKeys {
val SETTING_ONE = intPreferencesKey("setting_one")
}
// retrieve datastore for currently logged in user.
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = userRepository.currentRegistration().name)
val userPreferencesFlow: Flow<UserPreferences> = context.dataStore.data.map { preferences ->
val settingOne = preferences[PreferencesKeys.SETTING_ONE] ?: 0
UserPreferences(settingOne)
}
suspend fun storeSettingOne(settingOne: Int) {
context.dataStore.edit { preferences ->
preferences[PreferencesKeys.SETTING_ONE] = settingOne
}
}
data class UserPreferences(val lastUsedToAccountTab: Int)
}
I'm using Koin
and I tried unloading the DataStorageRepository
on logout and recreating it on login but the DataStore seems to stay alive until the app is killed and I get the following crash:
java.lang.IllegalStateException: There are multiple DataStores active for the same file: [...] You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).
I also tried to use a CoroutineScope
and kill that when I log out, but after recreating the scope on login the DataStore doesn't seem to get recreated.
Does DataStore support a way to close the connection or to handle multiple files?
Put this line inside companion object { }
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settingPrefs")
My Code
class SettingPrefs(private val context: Context) {
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settingPrefs")
private val soundKey = booleanPreferencesKey("sound")
private val vibrateKey = booleanPreferencesKey("vibrate")
}
val getSound: Flow<Boolean>
get() = context.dataStore.data.map {
it[soundKey] ?: true
}
suspend fun setSound(value: Boolean) {
context.dataStore.edit { it[soundKey] = value }
}
val getVibration: Flow<Boolean>
get() = context.dataStore.data.map {
it[vibrateKey] ?: true
}
suspend fun setVibration(value: Boolean) {
context.dataStore.edit { it[vibrateKey] = value }
}
}
You can use different key for different user or manual keep DataStore singleton.
For exception:
java.lang.IllegalStateException: There are multiple DataStores active for the same file: [...] You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).
androidx.datastore:datastore-*:1.0.0-alpha07
is released.
Put this at the top level of your kotlin file so there is only one instance of it.
private val Context.dataStore by preferencesDataStore("settings")
class Xxx{
}
https://developer.android.com/jetpack/androidx/releases/datastore#1.0.0-alpha07.
The Context.createDataStore extension function has been removed and replaced with globalDataStore property delegate. Call globalDataStore once at the top level in your kotlin file. For example:
val Context.myDataStore by dataStore(...)
Put this at the top level of your kotlin file so there is only one instance of it. (I57215, b/173726702)
At the moment I was posting this question I found a solution to this problem. In order to solve my problem I needed to combine my previous two solutions. So on logout I unload the DataStorageRepository
and on login I reload it again. I also needed to create a CoroutineScope
that I cancel on logout.
My Module
val loggedInModule = module {
single { DataStorageRepository(get(), get()) }
}
I created a scope and passed it to the DataStore
var loggedInScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = userRepository.currentRegistration().name, scope = loggedInScope)
On Login
loggedInScope = CoroutineScope(Dispatchers.Default)
loadKoinModules(loggedInModule)
On Logout
loggedInScope.cancel()
unloadKoinModules(loggedInModule)
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