Introducing DataStore

douglascf

Douglas Fornaro

Posted on December 29, 2020

Introducing DataStore

A new way to store data on Android.

Overview

  • A new and improved data storage solution aimed at replacing SharedPreferences.
  • Kotlin coroutines and Flow.
  • Data is stored asynchronously, consistently, and transactionally.

SharePreferences vs DataStore

Comparing SharedPreferences vs DataStore

Features

It provides two different implementations:

  • Preferences DataStore: stores primitive data key-value pairs.
  • Proto DataStore: stores typed objects.

In both implementations, DataStore saves the preferences in a file and performs all data operations on Dispatchers.IO unless specified otherwise.

Pre requirement

  • Familiarity with coroutines and Kotlin Flow.

Steps

Setup

Gradle:

// Preferences DataStore 
implementation β€œandroidx.datastore:datastore-preferences:1.0.0-alpha05”
// Proto DataStore 
implementation β€œandroidx.datastore:datastore-core:1.0.0-alpha05”
Enter fullscreen mode Exit fullscreen mode

For Proto DataStore:

Create the DataStore

Preference DataStore:

val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")
Enter fullscreen mode Exit fullscreen mode

Proto DataStore:

object SettingsSerializer : Serializer<Settings> {
  override fun readFrom(input: InputStream): Settings {
    try {
      return Settings.parseFrom(input)
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception)
    }
  }

  override fun writeTo(
    t: Settings,
    output: OutputStream) = t.writeTo(output)
}

val settingsDataStore: DataStore<Settings> = context.createDataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)
Enter fullscreen mode Exit fullscreen mode

Read data

Preference DataStore:

val MY_COUNTER = preferencesKey<Int>("my_counter")
val myCounterFlow: Flow<Int> = dataStore.data
     .map { currentPreferences ->
        // Unlike Proto DataStore, there's no type safety here.
        currentPreferences[MY_COUNTER] ?: 0   
   }
Enter fullscreen mode Exit fullscreen mode

Proto DataStore:

val myCounterFlow: Flow<Int> = settingsDataStore.data
    .map { settings ->
        // The myCounter property is generated for you from your proto schema!
        settings.myCounter 
    }
Enter fullscreen mode Exit fullscreen mode

Write data

Preference DataStore:

suspend fun incrementCounter() {
    dataStore.edit { settings ->
        // We can safely increment our counter without losing data due to races!
        val currentCounterValue = settings[MY_COUNTER] ?: 0
        settings[MY_COUNTER] = currentCounterValue + 1
    }
}
Enter fullscreen mode Exit fullscreen mode

Proto DataStore:

suspend fun incrementCounter() {
    settingsDataStore.updateData { currentSettings ->
        // We can safely increment our counter without losing data due to races!
        currentSettings.toBuilder()
            .setMyCounter(currentSettings.myCounter + 1)
            .build()
    }
}
Enter fullscreen mode Exit fullscreen mode

Migrate from SharedPreferences

Preference DataStore:

val dataStore: DataStore<Preferences> = context.createDataStore(
    name = "settings",
    migrations = listOf(SharedPreferencesMigration(context, "settings_preferences"))
)
Enter fullscreen mode Exit fullscreen mode

Proto DataStore:

val settingsDataStore: DataStore<Settings> = context.createDataStore(
    produceFile = { File(context.filesDir, "settings.preferences_pb") },
    serializer = SettingsSerializer,
    migrations = listOf(
        SharedPreferencesMigration(
            context,
            "settings_preferences"            
        ) { sharedPrefs: SharedPreferencesView, currentData: Settings ->
            // Map your sharedPrefs to your type here
          }
    )
)
Enter fullscreen mode Exit fullscreen mode

Conclusion

Jetpack DataStore is a replacement for SharedPreferences that address some problems such as a synchronous API that can appear to be safe to call on UI thread, no mechanism for signaling errors, lack of transactional and more. DataStore is fully asynchronous API using Kotlin Coroutines and Flow, consistent, handle data migration and data corruption.

πŸ’– πŸ’ͺ πŸ™… 🚩
douglascf
Douglas Fornaro

Posted on December 29, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Android Compose DataStore Tutorial
android Android Compose DataStore Tutorial

December 23, 2022

Introducing DataStore
android Introducing DataStore

December 29, 2020