Why Jetpack DataStore over SharedPreferences?

Tanushree
GDSC, IIIT Allahabad
5 min readApr 5, 2022

SharedPreferences are one of the easiest ways to store data locally as it requires very minimal overhead. However, there were certain issues with SharedPreferences such as dealing with uncaught exceptions and blocking the UI thread while making calls. To address these issues effectively, Jetpack DataStore has been introduced recently by Google as an advanced and enhanced data storage solution for replacing SharedPreferences, as it is thread-safe and non-blocking.

Let’s discuss what’s DataStore and why DataStore.

What is DataStore?

  • Jetpack DataStore is a new and improved data storage solution.
  • It allows us to store key-value pairs (like SharedPreferences) or typed objects with protocol buffers.
  • It uses Kotlin, Coroutines, and Flow to store data asynchronously with consistency and transaction support.
  • In short, it’s the new data storage solution that is the replacement of SharedPreferences.

Why DataStore?

  • SharedPreference has some drawbacks like it provided synchronous APIs -but it’s not MAIN-thread-safe! whereas DataStore is safe to use in UI thread because it uses Dispatchers.IO under the hood👀.
  • It’s safe from runtime exceptions! What would be more satisfying than this?
  • It also provides a way to migrate from SharedPreferences. More on this in later sections.
  • It provides Type safety! (Using Protocol buffers).

How to implement?

DataStore provides two different types of implementations to store data : Preference DataStore and Proto DataStore.

Preference DataStore

This uses key-value pairs to store data. But it doesn’t provide type-safety.

Setup

To use Jetpack Preferences DataStore in your app, add the following to your Gradle file:

Create a Preferences DataStore

Use the Context.createDataStore() extension function to create an instance of DataStore.

The mandatory name parameter is the name of the Preferences DataStore.

Read from a Preferences DataStore

Because Preferences DataStore does not use a predefined schema, you must use Preferences.preferencesKey() to define a key for each value that you need to store in the DataStore instance.

Then, use the DataStore.data property to expose the appropriate stored value using a Flow.

DataStore ensures that data is retrieved on Dispatchers.IO so your UI thread isn’t blocked.

Handling exceptions in DataStore

As DataStore reads data from a file, IOExceptions are thrown when an error occurs while reading data. We can handle these by using the catch() Flow operator before map() .

Write to a Preferences DataStore

To write data, DataStore offers a suspending DataStore.edit(transform: suspend (MutablePreferences) -> Unit) function, which accepts a transform block that allows us to transactionally update the state in DataStore.

All of the code in the transform block is treated as a single transaction.

Proto DataStore

It stores data as a custom type with specified schema using Protocol Buffers.

Setup

Start by adding the Proto DataStore dependency. If you’re using Proto DataStore, make sure you also add the proto dependency:

Add protopuf classpath in project build.gradle:

add protopuf plugins in your app build.gradle:

Also, make sure that you are using Java 1.8 version.

Define a schema

Proto DataStore requires a predefined schema in a proto file in the app/src/main/proto/ directory. This schema defines the type for the objects that you persist in your Proto DataStore.

Once, create proto file, android studio ask you to install proto plugin to support the proto file format.

just, click install to add proto plugin to install plugin.

Note: The class for your stored objects is generated at compile time from the message defined in the proto file. Make sure you rebuild your project.

Create a Preferences DataStore

Two steps involved in creating a Proto DataStore to store your typed objects:

Define a class that implements Serializer<T>,where T is the type defined in the proto file. This serializer class tells DataStore how to read and write your data type.

Use the Context.createDataStore() extension function to create an instance of DataStore, where T is the type defined in the proto file.

Read from a Preferences DataStore

Use DataStore.data to expose a Flow of the appropriate property from your stored object.

Write to a Preferences DataStore

Proto DataStore provides an updateData() function that transactionally updates a stored object. updateData() gives you the current state of the data as an instance of your data type and updates the data transactionally in an atomic read-write-modify operation.

Migrate from SharedPreferences to DataStore —

Migration is done when there is a need to change, upgrade or move. Data migration is the process of transferring data between data storage systems where the underlying data can change, which can affect the application layer when there is a change in protocol or data language.

-> To migrate from SharedPreferences to Preferences DataStore, we have to pass SharedPreferencesMigration object to the DataStore builder.

-> To migrate from SharedPreferences to Proto DataStore, we have to pass SharedPreferencesView and UserPreferences current data in the SharedPreferencesMigration class and returns a UserPreferences object.

After defining the migration logic, we need to tell DataStore that it should use it.

SharedPreferences vs DataStore

Image source: here

Room vs DataStore

If there is a need for partial updates, referential integrity, or support for larger datasets, then we consider Room instead of DataStore. DataStore doesn’t support partial updates and is ideal for small and simple datasets.

Conclusion

The DataStore library comes up with many benefits over the SharedPreferences API. In DataStore, data is stored asynchronously, consistently, and transactionally, overcoming most of the drawbacks of SharedPreferences.

Additional Resources

To get started, find out more about DataStore in official documentation and try it out by taking codelabs: Preferences DataStore codelab and Proto DataStore codelab.

References

--

--