Preferences Datastore is slow, but you should still choose it over Shared Preferences. Here’s why… | by Ishan Vohra | Feb, 2024

Why do we need a key-value storage framework?

Sometimes we need to store values corresponding to a key for easy access throughout which we can access anytime throughout the lifecycle of our app. For these kinds of values, a key-value storage framework is the most appropriate solution. They are quite efficient since they store data on a simple file and perform CRUD operations on the same.

But is the new guy better than the good ol’ Shared Preferences? Let’s find out!

To find the performance differences between both the storage frameworks, let’s define tests for read and write scenarios.

Structuring the tests

Both Shared Preference and Preferences Datastore, store values of primitive datatypes, such as, string, long, boolean, etc. Thus, our tests should cover reading and writing values belonging to above mentioned types.

Let’s create two functions for each library, one for reading and one for writing.

In the writeSharedPref() function, we are storing a large json string, a large number (of Long datatype) and a boolean, and in the readSharedPref() function, we’re reading the same values which we stored. Please note that, sharedPreferences object is already initialized before calling this function.

Now, let’s write the functions that store same objects but use Preferences Datastore.

Implementing Preferences Datastore is a little complicated than Shared Preferences. In the above code, the dataStoreManager object is an instance of the DatastoreManager class which uses the datastore instance to perform read and write operations. If you’d like to see the complete DatastoreManager class, click here.

I am using firstOrNull() function to get the value because Datastore returns a Flow whenever we read a value, which is a stream of data outputting values as they change. Since, we are storing data only once, calling firstOrNull() function returns the first value that comes up in the stream, i.e, the value we just stored.

Running the tests

After running the above functions for both frameworks, below are the average values:

The average writing times for different datatypes for Shared Preferences are:

Shared preferences — Execution times for write and read functions

The average writing times for different datatypes for Preferences Datastore are:

Preferences Datastore — Execution times for write and read functions

Conclusion from the tests

Are my eyes deceiving? Or the Shared Preferences actually faster than Preferences Datastore, which is actively pushed by Jetpack to be the de-facto key, value storage framework in android?

The reading times of Shared Preferences are lower than Preferences Datastore by a margin of 1 or 2 milliseconds but writing times is where Shared Preferences gain a lot of performance. For string values especially, the Preferences Datastore took almost 4 times as much time compared to Shared Preferences.

But the test doesn’t conclude here. We have established that Shared Preferences is much faster in read and write operations, but there’s more to the picture. We’ve only compared the performance with respect to the read and write times but what about the storage?

Comparing storage

We can find out how large each file is by finding out the length of the text stored in them. The Shared Preferences library, stores the data in XML format and thus, is available as such. In our case, the length of the XML content is 28,279 and for the Preferences Datastore library, which serializes the data stored in bytes and thus, is not readable but we can still find out the length of content in it, which turns out to be 15,548 in our case.

The latter created a smaller file due to the serialization format used. This is where Preferences Datastore justifies its complex serialization/de-serialization technique as it trades off a little bit of the performance for a smaller and secure data file.

Despite it’s lack of speed in write times, you should still choose Preferences Datastore as the key, value pair storage framework in your project and here’s why. (Hint: The key word is aysnc).

Asynchronous tasks: Preferences Datastore allows us to do data read and write operations asynchronously, meaning, we do not have to use them sequentially.

Thread safety: It’s safe to call on the UI thread since the work that it does is moved to the IO thread under the hood. Call it anywhere and it will not block the operations on the UI thread, providing a smooth user experience to the user. Whereas, Shared Preference is not safe to be used on the main thread and here’s how the official documentation explains it:

SharedPreferences has a synchronous API that can appear safe to call on the UI thread, but which actually does disk I/O operations. Furthermore, apply() blocks the UI thread on fsync(). Pending fsync() calls are triggered every time any service starts or stops, and every time an activity starts or stops anywhere in your application. The UI thread is blocked on pending fsync() calls scheduled by apply(), often becoming a source of ANRs.

Flows: What I feel is the biggest advantage of Preferences Datastore is that it uses Flow API while reading data. A flow is a stream of data which has a collector and producer. The producer sends the data and collector, collects the incoming value. If there’s a value which is going to keep changing, for example, an API response or an auth token, as the new value is stored on the file, the collect() function, gets the new value automatically without calling it again. Flow are also lifecycle unaware which means unlike LiveData , they are not dependent on the lifecycle fragment or activity they are being used in.

Preferences Datastore also provide more benefits such as:

  1. They can signal errors.
  2. They are safe from runtime exceptions.
  3. They have a strong transactional API with strong consistency guarantees.

So, what did we learn? Things aren’t always how they appear to be. I was going to conclude the tests at the writing/reading speeds, but the file size showed a bigger and more complete picture. If you’re looking for storing small values, which do not change often and need to access them on the UI thread, use Shared Preferences. But if you want to store more complex objects with larger values, and might need updating regularly, use Preferences Datastore.

To be honest, this article took me quite longer to finish than I originally anticipated. Before writing this article, I wasn’t aware how protobuf format actually worked, how different both frameworks are, etc. It took more time but was a good learning experience and hope it was for you as well. If you’re still reading, thank you and do hit the follow button and checkout this article where I describe how debouncing API calls can increase performance in your app or website!

Next Post

From Novice to Influencer: Secrets of Successful Social Media Content

Social media content has become the lifeblood of online marketing and personal branding, serving as a crucial bridge between brands and their audiences.  Try these social media tools: UseViral can enhance your social media presence by increasing your followers, likes, reach, views, engagement and more. InVideo generates fast videos with […]
From Novice to Influencer: Secrets of Successful Social Media Content

You May Like