Migrating SharedPreferences with Jetpack DataStore

Ade Dyas
Staffinc Tech
Published in
5 min readMay 18, 2021

As an Android developer, we are certainly familiar with SharedPreferences. It is often used to store small data since it is easier and more practical than using Databases. SharedPreferences performs data storage using the key-value pairs method, and data is stored using keys as markers.

However, SharedPreferences is considered too much of a performance drain and can hinder the performance of our application without us knowing it. SharedPreferences also runs on the UI Thread, allowing our apps to crash if something goes wrong while loading or saving data.

Google as the provider of the SharedPreferences API issued a new technology called Jetpack DataStore, which is considered to be able to solve problems that exist in SharedPreferences. DataStore runs in a background thread and is considered safe enough to run without disturbing the performance of our application.

In general, when we will use SharedPreferences, we will create a class that will make it easier to use SharedPreferences. I’ve created a sample project that illustrates the migration from SharedPreferences to Jetpack DataStore, which we can see here:

We will run the sample project and try out the whole function whether it is working properly to see the difference. The project above is a sample project from the BMI Calculator, where we will enter the height and weight, then the system will calculate it and provide suggestions based on the resulting value. The program flow in the project saves the user's data after pressing the Calculate button.

In the project, there are 2 classes,

  1. MainActivity
  2. BmiBrain

We need to pay attention is the use of SharedPreferences in the BmiBrain class. In this class, there are :

  1. Initialize SharedPreferences
  2. Load / Get value from SharedPreferences
  3. Save the value to SharedPreferences

The first step we do to perform the migration is adding the Jetpack DataStore library at the app level build.gradle

implementation “androidx.datastore:datastore-preferences:1.0.0-alpha08”

As of this writing, the latest version of Jetpack DataStore is 1.0.0-alpha08

After adding the above libraries, we will change the initialization of the SharedPreferences in the BmiBrain class with the Jetpack DataStore, as well as change the way to set and get values for each variable, because the Jetpack DataStore runs on a background thread, then we will wrap it with coroutines using the runBlocking and withContext functions. And use the getValueFlow extension to simplify and avoid boilerplate code.

We need to pay attention and how to call the set and get value functions. The Jetpack DataStore key-value does not use a String on its key like SharedPreferences but instead uses preferencesKey. Construct in preferencesKey is adjusted to the data type used. More details preferencesKey can be accessed through the link here, for example, because we will save the name, then what we use is stringPreferencesKey followed by the name of preferences such as “NAME_KEY,” etc. We don’t have to worry about data type errors when calling with the function.

After that, we need to initialize the DataStore by entering the name SharedPreferences we used earlier in the SharedPreferencesMigration function so that our previous data can be migrated to the dataStore automatically with the same key pair. It needs to be underlined. Google recommends that our DataStore is initialized as a singleton so that there is no conflict when there are 2 or more DataStores that are running and cause crashes or exceptions.
Since we are already using coroutines, we don’t need any changes on the caller side or in this case MainActivity because basically, we are calling synchronously.

When we try to run, the application can load existing values when using SharedPreferences, even though we are no longer using them. However, the solution we are working on is not the best because it is still synchronous, so how do we make it asynchronous? We do remove the coroutines while doing the get and set values and make it a suspend function.

After that, because the suspend function only runs in the background task, we need to change the caller side MainActivity to:

Don’t forget that because we are going to use the lifecycleScope function, we need to add the library

implementation “androidx.lifecycle:lifecycle-runtime-ktx:2.3.1”

Then we try to rerun it, whether the application runs properly.

  • With SharedPreferences
  • With Jetpack DataStore

Comparison

We can see it using the device explorer menu on Android Studio with the location to compare the storage results.

/data/data/{project id}/shared_prefs/{SharedPreferences name with extension .xml}

As for the Jetpack DataStore, it will be available with the following locations:

/data/data/{project id}/files/datastore/{DataStore name followed with .preferences_pb}

If it’s not there, try disconnecting the USB cable or turning off debug mode on android first, then reconnecting it.

It looks encrypted for numeric and boolean data types, making it safer. The memory size is more compact (126 kb) than using SharedPreferences which reaches 351 kb for the same data.

TL;DR

The DataStore provides a migration function from SharedPreferences so that Android developers find it easier to implement this new API from Google.

Update

If you prefer not to use runBlocking or Dispatchers, you can also take advantage of the ViewModel class in this way:

After that, you can modify the MainActivity like this :

References

--

--