Preference Datastore Android — An implementation guide

A new and improved way to store data.

Amir Raza
3 min readDec 28, 2020
Image from here

What is Datastore?

Datastore is a new and improved data storage solution aimed at replacing Shared Preferences. Built on Kotlin coroutines and Flow.

There are two types of Datastore provided by google: Proto Datastore and Preference Datastore.

Points to note here:

  • Preference Datastore is relatively simple and easy to use where Proto Datastore takes a bit longer to configure.
  • Proto Datastore uses schema to store objects and it generate classes at compile time, so you have to rebuild project every time you change in schema.
  • As Proto Datastore uses schema to store whole object, that’s why Its type-safe, where Preference Datastore uses key-value pairs (No type safety).
  • In both types, Data is stored asynchronously, transactionally and consistently that solves problems the Shared Preferences have.

You can find plenty of theory about it on the internet. So, without wasting time, let’s do the practice step-by-step.

1Add the following dependency in app level build.gradle and sync project.

dependencies {   //…
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
}

You can find the latest dependency version here

2Create datastore instance to read and write data. Let’s create a class to manage reading and writing data.

class PreferenceManager(val context: Context) {

val datastore = context.createDataStore(name = "SettingsPrefs")
}

name is the preferences datastore file name

That’s it! 🙏 You are done with configurations. Let’s start reading and writing data from Preference Datastore.

✒️ Preferences Datastore uses preferencesKey<T>() rather normal key-value pairs. A special method for preferences datastore.

Reading data from Preference Datastore

Preferences Datastore provides 2 ways to read data.

  1. It exposes the data stored in a streamed way, a Flow<T> typed that will emit every time a preference has changed. So, we do not need to read the whole preference object.
  2. It exposes data in a standalone way, the whole preference then we extract key-value paired data.

Create an object class to maintain your preferencesKey<T>()

object Keys {
val isDarkTheme = preferencesKey<Boolean>("IS_DARK_THEME")
val authToken = preferencesKey<String>("AUTH_TOKEN")
.
.
.
}

Now, Let’s create a public method to get data in Flow<T> way.

val isDarkThemeFlow : Flow<Boolean> = datastore.data
.map { pref ->
pref
[Keys.isDarkTheme] ?: false
}

Then, you can use kotlin’s coroutines suspending function collect to receive data or can observe in UI by converting Flow toasLiveData()

Handling exception while reading data:

As Datastore reads data from a file, IOException are thrown when an error occurs while reading data. We can handle these by using the catch Flow before map and can emit emptyPreferences() or throw exception.

val isDarkThemeFlow : Flow<Boolean> = datastore.data
.catch { e ->
e.printStackTrace()
emit(emptyPreferences())
}
.map { pref ->
pref[Keys.isDarkTheme] ?: false
}

Reading data from Preference Datastore can be in standalone [Without Flow]:

To read data as standalone, Datastore provide a suspending first() function, where we get the data from store [Does not stream]. let’s create a method from where we get data from preference datastore.

suspend fun getIsDarkTheme() : Boolean {
val preferences = datastore.data.first()
return preferences[Keys.isDarkTheme] ?: false
}

Writing data to Preference Datastore

To write data, Datastore provide a suspending edit() function, where we get MutablePreferences as parameter the current state of specific key. So, we use MutablePreferences to update the value of specific key.

suspend fun setIsDarkTheme(isDarkTheme: Boolean) {
datastore.edit { pref ->
pref
[Keys.isDarkTheme] = isDarkTheme
}
}

To remove any specific key:

datastore.edit {
it
.remove(Keys.isDarkTheme)
}

To clear the preferences file:

datastore.edit {
it
.clear()
}

--

--