You are on page 1of 16

31/03/2024, 11:50 All about Preferences DataStore.

aStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Open in app Sign up Sign in

Search

All about Preferences DataStore


Simona Milanović · Follow
Published in Android Developers
6 min read · Jan 24, 2022

Listen Share

In this post, we will take a look at Preferences DataStore, one of two DataStore
implementations. We will go over how to create it, read and write data, and how to
handle exceptions, all of which should, hopefully, provide you with enough
information to decide if it’s the right choice for your app.

Preferences DataStore uses key-value pairs to store smaller datasets, without


defining the schema upfront. This might remind you of SharedPreferences , but only
in the way it structures your data models. There are a variety of benefits brought by
DataStore over its SharedPreferences predecessor. Feel free to quickly jump back to
our previous post and take a look at the detailed comparison we’ve made there.
Going forward, we will refer to Preferences DataStore as just Preferences , unless
specified otherwise.

To quickly recap:

Provides a fully asynchronous API for retrieving and saving data, using the
power of Kotlin coroutines

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 1/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Does not offer ready-to-use synchronous support — it directly avoids doing any
work that blocks the UI thread

Relies on Flow’s inner error signaling mechanism, allowing you to safely catch
and handle exceptions when reading or writing data

Handles data updates transactionally in an atomic read-modify-write operation,


providing strong ACID guarantees

Allows easy and quick data migrations

Wish to quickly migrate from SharedPreferences with minimal changes and feel
confident enough without full type safety? Choose Preferences over Proto

Now let’s dive into some code and learn how Preferences should be implemented.

We will use the Preferences DataStore codelab code. If you’re interested in a more
hands-on approach with implementation, we really encourage you to go through the
Working with Preferences DataStore codelab on your own.

This sample app displays a list of tasks and the user can choose to filter them by
their completed status or sort by priority and deadline. We want to store their
selection — a boolean for completed tasks and a sort order enum in DataStore.

Dependency setup
Let’s start with adding the necessary dependency:

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 implementation "androidx.datastore:datastore-preferences:1.0.0"

build.gradle hosted with ❤ by GitHub view raw

💡 Quick tip — if you want to minify your build, make sure to add an additional rule
to your proguard-rules.pro file to prevent your fields from being deleted:

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 2/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 -keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessa
5 <fields>;
6 }

proguard-rules.pro hosted with ❤ by GitHub view raw

Creating a Preferences DataStore


You interact with data saved in DataStore through an instance of
DataStore<Preferences> . DataStore is an interface that grants access to the persisted
information. Preferences is an abstract class similar to a generic Map, used
specifically in the Preferences implementation of DataStore to keep track of your
data’s key-value pairs. We will talk about its MutablePreferences subclass when we
discuss writing data.

To create this instance, it is recommended to use the delegate preferencesDataStore

and pass a mandatory name argument. This delegate is a Kotlin extension property
whose receiver type must be an instance of Context , subsequently needed for
constructing the File object, where DataStore stores the data:

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 private val Context.dataStore by preferencesDataStore(
5 name = USER_PREFERENCES_NAME
6 )

TasksActivity.kt hosted with ❤ by GitHub view raw

You shouldn’t create more than one instance of DataStore for a given file, as doing
so can break all DataStore functionality. Therefore, you can add the delegate
construction once at the top level of your Kotlin file and use it throughout your
application, in order to pass it as a singleton. In later posts, we will see how to do
this with dependency injection.

Defining keys
DataStore provides a quick way of constructing keys for different data types, such as
booleanPreferencesKey , intPreferencesKey and many more — you just need to pass
the key name as value. While this does put some constraints on data types, keep in

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 3/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

mind that it doesn’t provide definite type safety. By specifying a preference key of a
certain type, we hope for the best and rely on our assumptions that a value of a
certain type would be returned. If you feel your code is structured in a way to
handle this safely, feel free to continue with Preferences. If not, consider using
Preferences’ sibling, Proto DataStore, as it provides full type safety.

In our app’s UserPreferencesRepository , we specify all the keys needed for


structuring the key-value pairs of our persisted data:

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 private object PreferencesKeys {
5 val SORT_ORDER = stringPreferencesKey("sort_order")
6 val SHOW_COMPLETED = booleanPreferencesKey("show_completed")
7 }

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

Reading data
To read the stored data, in UserPreferencesRepository we expose a Flow<Preferences>

from dataStore.data . This provides efficient access to the latest saved state and
emits with every change. Using Kotlin data classes, we can observe any emissions
and transform the provided DataStore Preferences object into our own
UserPreferences model, using only the key-value pairs we’re interested in:

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 val userPreferencesFlow: Flow<UserPreferences> = dataStore.data.map { preferences ->
5 val sortOrder = SortOrder.valueOf(preferences[PreferencesKeys.SORT_ORDER] ?: SortOrd
6 val showCompleted = preferences[PreferencesKeys.SHOW_COMPLETED] ?: false
7
8 UserPreferences(showCompleted, sortOrder)
9 }

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

The Flow will always either emit a value or throw an exception when attempting to
read from disk. We will look at exception handling in later sections. DataStore also

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 4/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

ensures that work is always performed on Dispatchers.IO so your UI thread isn’t


blocked.

🚨 Do not create any cache repositories to mirror the current state of your
Preferences data. Doing so would invalidate DataStore’s guarantee of data
consistency. If you require a single snapshot of your data without subscribing to
further Flow emissions, prefer using dataStore.data.first() :

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 // Don't
5 suspend fun fetchCachedPrefs(scope: CoroutineScope): StateFlow<Preferences> = dataStore.
6 // Do
7 suspend fun fetchInitialPreferences() = mapUserPreferences(dataStore.data.first().toPref

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

Writing data
For writing data, we will use a suspend DataStore<Preferences>.edit(transform:

suspend (MutablePreferences) -> Unit) function. Let’s break that down:

DataStore<Preferences> interface — we’re currently using datastore as the


concrete Preferences implementation

transform: suspend (MutablePreferences) -> Unit — a suspend block used to


apply the specified changes to our persisted data

MutablePreferences — a mutable subclass of Preferences , similar to MutableMap ,

that allows us to make changes to our key-value pairs

As an example, we will change our SHOW_COMPLETED flag:

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 5/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 suspend fun updateShowCompleted(showCompleted: Boolean) {
5 dataStore.edit { preferences ->
6 preferences[PreferencesKeys.SHOW_COMPLETED] = showCompleted
7 }
8 }

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

Editing data is done transactionally in an atomic read-modify-write operation. This


means that the specific order of data processing operations, during which the data
is locked for other threads, guarantees consistency and prevents race conditions.
Only after the transform and edit coroutines complete successfully, the data will be
persisted durably to disk and datastore.data Flow will be reflecting the update.

🚨 Keep in mind that this is the only way of making changes to the DataStore state.
Keeping a MutablePreferences reference and mutating it manually after transform

completes will not change the persisted data in DataStore, so you shouldn’t attempt
to modify MutablePreferences outside of the transform block.

If the writing operation fails for any reason, the transaction is aborted and an
exception is thrown.

Migrate from SharedPreferences


If you’ve previously used SharedPreferences in your app and would like to safely
transfer its data to Preferences, you can use SharedPreferencesMigration . It requires a
context, SharedPreferences name and an optional set of keys you wish to migrate (or
just leave it as the default MIGRATE_ALL_KEYS value).

Looking into SharedPreferencesMigration implementation, you will see a


getMigrationFunction() which is responsible for fetching all required, stored key-
value pairs and then adding them to Preferences using the same keys. Pass
SharedPreferencesMigration via the produceMigrations parameter of the ​
preferencesDataStore delegate to migrate easily:

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 6/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 private val Context.dataStore by preferencesDataStore(
5 name = USER_PREFERENCES_NAME,
6 produceMigrations = { context ->
7 listOf(SharedPreferencesMigration(context, USER_PREFERENCES_NAME))
8 }
9 )

TasksActivity.kt hosted with ❤ by GitHub view raw

produceMigrations will ensure that the getMigrationFunction() is run before any


potential data access to DataStore. This means your migration must have succeeded
before DataStore emits any further values and before it begins making any new
changes to the data. Once you’ve successfully migrated, it’s safe to stop using
SharedPreferences , as the keys are migrated only once and then removed from
SharedPreferences .

The produceMigrations accepts a list of DataMigration . We will see in later episodes


how we can use this for other types of data migrations. If you don’t need to migrate,
you can ignore this as it has a default listOf() provided already.

Exception handling
One of the main advantages of DataStore over SharedPreferences is its neat
mechanism for catching and handling exceptions. While SharedPreferences throws
parsing errors as runtime exceptions, leaving room for unexpected, uncaught
crashes, DataStore throws an IOException when an error occurs with
reading/writing data.

We can safely handle this by using the catch() Flow operator right before map()

and emitting emptyPreferences() :

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 7/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 val userPreferencesFlow: Flow<UserPreferences> = dataStore.data.catch { exception ->
5 // dataStore.data throws an IOException when an error is encountered when reading dat
6 if (exception is IOException) {
7 Log.e(TAG, "Error reading preferences.", exception)
8 emit(emptyPreferences())
9 } else {
10 throw exception
11 }
12 }.map { preferences ->
13 mapUserPreferences(preferences)
14 }

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

Or with a simple try-catch block on writing:

1 /* Copyright 2022 Google LLC.


2 SPDX-License-Identifier: Apache-2.0 */
3
4 suspend fun updateShowCompleted(showCompleted: Boolean) {
5 try {
6 dataStore.edit { preferences ->
7 preferences[PreferencesKeys.SHOW_COMPLETED] = showCompleted
8 }
9 } catch (e: IOException) {
10 // Handle error
11 }
12 }

UserPreferencesRepository.kt hosted with ❤ by GitHub view raw

If a different type of exception is thrown, prefer re-throwing it.

To be continued
We’ve covered DataStore’s Preferences implementation — when and how to use it
for reading and writing data, how to handle errors and how to migrate from
SharedPreferences . In the next post, we will cover the same topics with the Proto
DataStore implementation, so stick around.

You can find all posts from our Jetpack DataStore series here:
Introduction to Jetpack DataStore

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 8/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

All about Preferences DataStore


All about Proto DataStore
DataStore and dependency injection
DataStore and Kotlin serialization
DataStore and synchronous work
DataStore and data migration
DataStore and testing

Mad Skills Datastore Featured Latest Android

Follow

Written by Simona Milanović


1.3K Followers · Writer for Android Developers

Android Developer Relations Engineer @Google, working on Jetpack Compose

More from Simona Milanović and Android Developers

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 9/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Simona Milanović in Android Developers

DataStore and dependency injection


In the following posts from our Jetpack DataStore series, we will cover several additional
concepts to understand how DataStore interacts…

4 min read · Feb 8, 2022

271 4

Ben Trengrove in Android Developers

Jetpack Compose: Strong Skipping Mode Explained


https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 10/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Strong skipping mode changes the rules for what composables can skip recomposition and
should greatly reduce recomposition.

13 min read · Feb 28, 2024

1K 7

Rebecca Franks in Android Developers

Fun with shapes in Compose


Create a progress bar that transitions from a squiggly “star” shaped rounded polygon to a
circle while performing the regular progress…

6 min read · Feb 27, 2024

951 3

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 11/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Simona Milanović in Android Developers

All about Proto DataStore


In this post, we will learn about Proto DataStore, one of two DataStore implementations. We will
discuss how to create it, read and write…

9 min read · Jan 31, 2022

176 4

See all from Simona Milanović

See all from Android Developers

Recommended from Medium

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 12/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Michihiro Iwasaki

Persistent Data Storage Using DataStore (Preferences) in Jetpack


Compose
❚ ℹ: Sometimes, I may make mistakes or share information that isn’t completely accurate, as
I’m not perfect. I strive to minimize errors…

11 min read · Feb 27, 2024

66 1

Anna Karenina Jusuf

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 13/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

The Simplest Way to Use Preferences DataStore


As per August 2021, Android has release the new library called Jetpack DataStore, it is a new
and improved data storage solution aimed at…

3 min read · Nov 12, 2023

Lists

Staff Picks
609 stories · 872 saves

Mayur Waghmare in Mobile Innovation Network

SharedPreferences vs DataStore: Choosing the Right One


For years, developers have relied on SharedPreferences as a lightweight solution for storing
key-value pairs. However, with the evolution…

3 min read · Feb 28, 2024

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 14/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Ivan Dimitrov

SharedPreferences vs. DataStore: Evolving Data Management


In the realm of Android development, managing user preferences and app data efficiently is
crucial for crafting seamless user experience.

7 min read · Feb 15, 2024

Manu Aravind

Proto Datastore implementation in Android


https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 15/16
31/03/2024, 11:50 All about Preferences DataStore. In this post, we will take a look at… | by Simona Milanović | Android Devel…

Datastore is a replacement for SharedPreferences to overcome its shortcomings. Datastore is


an advanced data storage solution that was…

5 min read · Jan 1, 2024

11 1

Ryan W

Android SharedPreferences and Jetpack Preferences Datastore


This article dives into the traditional SharedPreferences and the more recent Jetpack
Preferences Datastore. It provides insights into…

· 4 min read · Mar 22, 2024

See more recommendations

https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334 16/16

You might also like