Hello DataStore, Bye SharedPreferences👋 — Android📱 — Part 1: Preference DataStore

Hello DataStore, Bye SharedPreferences👋 — Android📱 — Part 1: Preference DataStore

Welcome Android developers 👋. This article is the first part of a series article based on the new Jetpack library🚀 i.e. DataStore🗄️. Currently, it’s alpha version is released. Let’s see what’s DataStore and why DataStore.

What is DataStore 🤷‍♀️?

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

Why DataStore 🤷‍♂

  • First and my favourite reason 😃 — Built with❤️ Kotlin, Coroutines and Flow.
  • If you have used SharedPreferences you might abuse or blamed it for something 😆 then DataStore is here to rescue!
  • 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 that? 😅
  • It also provides a way to migrate from SharedPreferences 😍.
  • It provides Type safety!(Using Protocol buffers).

These are some reasons which encourage us to use DataStore and finally say goodbye to beloved SharedPreferences 👋.

# That’s not only the reason —

DataStore provides two different types of implementations to store data.

  • Preference DataStore— This uses key-value pairs to store data. But it doesn’t provide type-safety :
  • Proto DataStore —It stores data as a custom type with specified schema using Protocol Buffers (We’ll see about it in the next article).

I think that’s enough introduction to DataStore. It’s time to write some code👨‍💻😎.

Let’s begin code 👨‍💻

You can simply clone or refer this repository to get example code demonstrating DataStore📁.

We’ll develop a sample Android application which stores a UI mode preference from user i.e. 🌞 Light Mode or 🌑 Dark Mode.

First of all, let’s add a Gradle dependency in build.gradle of your app module. Currently 1.0.0-alpha01 is the latest release. You can keep an eye here to get info about the latest version.

dependencies {
    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"

Start implementing DataStore 📁

  • For UI mode preference i.e. Dark mode or Light mode, we’ll create an enum class as below
enum class UiMode {
  • We’ll create a class — SettingsManager where we’ll be managing setting preferences set by users in our app.
  class SettingsManager(context: Context) {

      private val dataStore = context.createDataStore(name = "settings_pref")

This will initialize the instancedataStore field by creating DataStore using the file name as “settings_pref”.createDataStore()is extension function created on Context.

  • Now we’ll be storing UI mode preference using a key (as we managed in SharedPreference). Key in DataStore is created as below 👇
companion object {
    val IS_DARK_MODE = preferencesKey<Boolean>("dark_mode")

Here 👆 we’ve created a 🔑 KEY IS_DARK_MODE which will store a boolean value (falsefor Light mode /truefor Dark mode). 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<Preferences>.

  • Now we’ll create a method which will set UI mode from our UI/Activity i.e. setUiMode() 🔧

Note: Preferences DataStore provides a method edit() which transactional updates value in DataStore.

suspend fun setUiMode(uiMode: UiMode) {
    dataStore.edit { preferences ->
        preferences[IS_DARK_MODE] = when (uiMode) {
            UiMode.LIGHT -> false
            UiMode.DARK -> true
  • Now it’s time to get preference 🔥. DataStore provides data property which exposes the preference values using Flow. Means it’s time to leverage Flow 🌊 😍. See the code below 👇
val uiModeFlow: Flow<UiMode> = dataStore.data
    .catch {
        if (it is IOException) {
        } else {
            throw it
    .map { preference ->
        val isDarkMode = preference[IS_DARK_MODE] ?: false

        when (isDarkMode) {
            true -> UiMode.DARK
            false -> UiMode.LIGHT

👆 You can see we’ve exposed a Flow uiModeFlow which will emit values whenever preferences are edited/updated. If you remember, we have been storing boolean in our DataStore. Using map{}, we’re mapping boolean values to theUiMode i.e UiMode.LIGHT or UiMode.DARK.

Note: DataStore throws IOException when it failed to read a value. So we have handled it by emitting emptyPreferences().

So that’s all about setting up DataStore 😃. Now let’s design UI.

Setup Activity

In this activity, We have just an ImageButton which will have image resources i.e. 🌞 and 🌘 based on UI mode.

class MainActivity : AppCompatActivity() {

    private lateinit var settingsManager: SettingsManager
    private var isDarkMode = true

    override fun onCreate(savedInstanceState: Bundle?) {

        settingsManager = SettingsManager(applicationContext)

  • In initViews() we’ll update preferences (UI Mode) on click of ImageButton.
      private fun initViews() {
          imageButton.setOnClickListener {
              lifecycleScope.launch {
                  when (isDarkMode) {
                      true -> settingsManager.setUiMode(UiMode.LIGHT)
                      false -> settingsManager.setUiMode(UiMode.DARK)
  • In observeUiPreferences(), we’ll observe DataStore UI Mode preference using a field which we exposed in SettingsManager which is a Flow 🌊 which will emit values whenever preferences are updated.
      private fun observeUiPreferences() {
          settingsManager.uiModeFlow.asLiveData().observe(this) { uiMode ->
              when (uiMode) {
                  UiMode.LIGHT -> onLightMode()
                  UiMode.DARK -> onDarkMode()

👆 Here we’ve used asLiveData() flow extension function which gives emitted values from Flow in LiveData. (Otherwise, we can also uselifecycleScope.launch{}here if you don’t like to useLiveData).

  • We’re just updating image resource and background color of root layout when UI mode is changed. (Actual mode can be changed usingAppCompatDelegate.setDefaultNightMode())
      private fun onLightMode() {
          isDarkMode = false

          rootView.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white))

      private fun onDarkMode() {
          isDarkMode = true

          rootView.setBackgroundColor(ContextCompat.getColor(this, android.R.color.black))

Yeah! That’s it 😃. It’s time to run this app 🚀. When you run this app, you’ll see it like 👇

Example app demonstrating use of Jetpack **DataStore**

Looking Awesome! 😍, isn’t it?

This is how we implemented Preferences DataStore instead of SharedPreferences.

So, DataStore is cool 🆒, isn’t it? Give it a try 😃. Since it’s currently in alpha, maybe many more is on the way to come 🛣️.

DataStore uses file management mechanism for storing data. But it’s different than managed in SharedPreferences. Now if you want to see how’s your data getting stored then using Android Studio’s ‘Device File Explorer’ you can go to /data/app/YOUR_APP_PACKAGE_NAME/files/datastore and you can see the file there like below 👇.

Device File Explorer Snapshot

But it’s content is not readable as you can see in below image 👇

Let me know your valuable feedback about this article. 🙏

In the next article, we’ll see how to use Proto DataStore.

Thank you! 😃


Did you find this article valuable?

Support Shreyas Patil by becoming a sponsor. Any amount is appreciated!