Meet DataStore - The New SharedPreferences for Android
To store some little amount of data in Android like phone number, name, or a person, etc. Android developers use SharedPreferences
API. But due to security and asynchronous transactions purpose, Google recommends using DataStore instead. In this article, we will discuss the disadvantages of SharedPreferences
, and some basics of the new DataStore API with its usage.
A Bit About SharedPreferences
The SharedPreferences APIs are for reading and writing key-value pairs stored in an XML based file in the data
directory of Android device. The SharedPreferences
allows app developers to store small amount of data like String, Int, Long, Double etc. for things like configs, settings etc. You can still put complex custom class objects through Serializable
, but that’s not usually recommended.
To use it, first you need to get an instance of SharedPreferences
and then you can either read the data with the get*()
methods like example below.
val preferences = this.getSharedPreferences("SOME_NAME", Context.MODE_PRIVATE)
// Reading the values from Preferences
val myStr = preferences.getString("myStrKey", "DEFAULT_STR")
val myLong = preferences.getLong("myLongKey", 0)
val myInt = preferences.getInt("myIntKey", 1)
val myDouble = preferences.getDouble("myDoubleKey", 0.0)
Or you can write the values in it through accessing the SharedPreferences.Editor
object and calling put*()
methods, and finally you can save those changes through the commit
or apply()
methods.
val preferences = this.getSharedPreferences("SOME_NAME", Context.MODE_PRIVATE)
// Get the Editor of SharedPreferences
val editor = preferences.edit()
// Save values through put*() methods
editor.putString("myStrKey", “Some String Value Goes Here”)
editor.putLong("myLongKey", 12L)
editor.putInt("myIntKey", 10)
editor.putDouble("myDoubleKey", 2.0)
// Save the above values in Preferences
editor.apply()
DataStore - An Alternative to SharedPreferences
The SharedPreferences
are for accessing persistent data and are loaded in the memory for fast access. So if you try to store large amount of data like custom data class objects, it is going to cause some more usage of critical memory of your app and will freeze the UI as data is access on Main Thread. It also doesn’t provide any error handling support. Another disadvantage with SharedPreferences
is type safety. It doesn’t provide the compiler-time support for type, so as a developer you have to be extra careful to see what key are you using to access the data. Because if you provide wrong key with wrong data, then it will cause a Runtime exception crash.
The DataStore is a new Async API for storing and reading data using Kotlin Coroutines and Flow, and this makes the DataStore thread-safe as it runs completely on a separate thread. It comes with a structured error handling, thread-safety, type-safety and support to store custom complex or large data class objects as well.
There are two main types of DataStore.
Preference DataStore: This stores data iin key-pair value and is more like DataStore version of SharedPreferences. This is best used for small amount of data as it doesn’t support type-safety and do not need any pre-defined schema to store the data.
Proto DataStore: It provides the type safety, and generates the schema to store the object that is passed to it. This allows you to save any kind of data whether it’s primitive like String, Long etc. or it can be complex custom class objects.
Preferences DataStore
As we saw in the previous example about SharedPreferences, let’s see how we can read write data with Preferences DataStore. First, we have to get the DataStore<Preferences>
object. We can do that by using the Kotlin delegate by preferencesDataStore
. This requires a name as SharedPreferences did. You can provide any name like “settings” or package name etc.
private val settingsDataStore by preferencesDataStore(name = “app_settings”)
Since Preferences DataStore is a key-value type data storage, so we need keys to read the data. In the SharedPreferences, we were using simple String keys. But with DataStore, we need to get the key through stringPreferenceKey
method. Note that you can also get other keys for Int, Long through intPreferenceKey
, longPreferenceKey
and so on.
val myStrKey = stringPreferencesKey("myStrKey")
val myLongKey = longPreferencesKey("myLongKey")
val myIntKey = intPreferencesKey("myIntKey")
Now you can use these key objects to read the Preferences from DataStore like this:
// Get the DataStore object
private val settingsDataStore by preferencesDataStore(name = “app_settings”)
// Get the keys
val myStrKey = stringPreferencesKey("myStrKey")
// Read the values through coroutine scopes
lifecycleScope.launch {
settingsDataStore.data.map { pref ->
// Read the values here
val myStr = pref[myStrKey]
}
}
The writing or storing to Preferences DataStore works in the same way except we call edit
method instead of the map
method like the code below:
// Get the DataStore object
private val settingsDataStore by preferencesDataStore(name = “app_settings”)
// Get the keys
val myStrKey = stringPreferencesKey("myStrKey")
// Write the values through coroutine scopes
lifecycleScope.launch {
settingsDataStore.edit { pref ->
// Write the values here
pref[myStrKey] = “SOME VALUE TO WRITE HERE”
}
}
So that’s how SharedPreferences and Preferences DataStore works. You can read more about in the official docs here. I will discuss the reading/writing of complex custom class objects in the Proto DataStore in the next article.
At the end, please Subscribe to my newsletter #Time with Wajahat to learn learn about the life experiences, lessons, career advices, technology & programming tips manually handcrafted and curated by Wajahat Karim.