Search code examples
androidandroid-jetpackkoincoroutinescopeandroid-jetpack-datastore

How to use Android DataStore with multi users or files


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?


Solution

  • 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)