androidcoroutinedatastore

Android DataStore read triggered by different key write


By looking the document, DataStore, I have the setup

build.gradle.kts

implementation ("androidx.datastore:datastore-preferences:1.0.0")

MainActivity.kt

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    val MY_COUNTER_1 = intPreferencesKey("my_counter_1")
    val MY_COUNTER_2 = intPreferencesKey("my_counter_2")

    lifecycleScope.launch {
        dataStore.data
            .map { currentPreferences ->
                // Unlike Proto DataStore, there's no type safety here.
                currentPreferences[MY_COUNTER_1] ?: 0
            }.collect {
                Log.d("MainActivity", "Counter 1: $it")
            }
    }

    lifecycleScope.launch {
        dataStore.data
            .map { currentPreferences ->
                // Unlike Proto DataStore, there's no type safety here.
                currentPreferences[MY_COUNTER_2] ?: 0
            }.collect {
                Log.d("MainActivity", "Counter 2: $it")
            }
    }

    lifecycleScope.launch {
        while (true) {
            dataStore.edit { currentPreferences ->
                val currentCounterValue = currentPreferences[MY_COUNTER_2] ?: 0
                currentPreferences[MY_COUNTER_2] = currentCounterValue + 1
            }
            delay(3000)
        }
    }

I found that when MY_COUNTER_2 is written with new values, MY_COUNTER_1 read is also triggered. I expected to see MY_COUNTER_2 read only, do I mis-config my DataStore?


Solution

  • What the documentation says about the data function:

    val data: Flow
    Provides [...] access to the latest durably persisted state.
    Returns a flow representing the current state of the data

    This sounds like the Flow will emit a new value whenever any value in the DataStore changes. The newly emitted value will be an object that holds all current preferences that are stored in the DataStore. You then filter the desired preference by its key, and collect will always process the value, even if the value actually has not changed.

    If you want to collect the value of a specific preference only if it actually changes, try out the distinctUntilChanged function:

     dataStore.data
        .map { currentPreferences ->
            currentPreferences[MY_COUNTER_1] ?: 0
        }.distinctUntilChanged().collect {
            Log.d("MainActivity", "Counter 1: $it")
        }