DataStore Tutorial For Android: Getting Started

In this tutorial you’ll learn how to read and write data to Jetpack DataStore, a modern persistance solution from Google. By Luka Kordić.

4.2 (9) · 1 Review

Download materials
Save for later
Share

DataStore is Google’s new and improved solution for persisting simple pieces of data by using either key-value pairs or protocol buffers for storing typed objects. It does so using Kotlin Coroutines and Flow to make all the transactions asynchronous, making all the data storing and fetching operations more performant and safe! It’s part of the Jetpack set of tools, so it’s also known as the Jetpack DataStore.

In this tutorial, you’ll learn how to:

  • Store simple key-value pairs to the Jetpack DataStore.
  • Store more complex, typed data to the Jetpack DataStore.
  • Migrate existing data from Shared Preferences to the Jetpack DataStore.
  • and what Protocol Buffers are.

Along the way, you’ll build an app that shows and filters a list of courses and supports dark mode.

Note: This tutorial assumes you know the basics of Android development. If you’re new to Android development, check out this Android Tutorial for Beginners. You should also be familiar with the basics of Kotlin Coroutines and Flow.

Getting Started

Download the materials by clicking the Download Materials button at the top or bottom of the tutorial. Then open the starter project in Android Studio 4.1 or later and look through its content.

Once the project opens and syncs you’ll see this package structure:

Project structure tree

This project uses ViewModel and LiveData Android Architecture Components, Model-View-ViewModel, or MVVM, architecture and Hilt for dependency injection.

Build and run. You’ll see a screen with the list of predefined courses, some filtering options on the top and a theme change option in the options menu.

Main screen with the list of predefined courses

Before adding any Kotlin code, configure Android Studio to insert import statements automatically.

Enabling Auto Import

Enabling Auto Import saves you from adding every individual import. If you already have Auto Import set, you can skip this paragraph and move to Implementing Theme Change.

If you’re on a Mac, go to Android StudioPreferences. On a PC, go to FileSettings. Then go to EditorGeneralAuto Import.

Under the Kotlin subheading, find Add unambiguous imports on the fly and Optimize imports on the fly (for current project). Check them both. Finally, click OK to save the settings.

Settings for Auto Import in Jetpack DataStore project

With all that set, it’s time to dive into the coding.

Implementing Theme Change

You’ll start by implementing the theme change functionality and storing the current theme in SharedPreferences.

In learningcompanion/presentation open CoursesViewModel.kt. Add the following below the class declaration:

private val _darkThemeEnabled = MutableLiveData<Boolean>()
val darkThemeEnabled: LiveData<Boolean> = _darkThemeEnabled

init {
    _darkThemeEnabled.value = sharedPrefs.isDarkThemeEnabled()
}

Here you create MutableLiveData that will hold the theme information, called _darkThemeEnabled. Since you don’t want to allow changes to this value from outside the file, you make it private.

Then, you create a public, immutable value you’ll observe in CoursesActivity.kt, named darkThemeEnabled. This approach is common in Android, where you have an underscored property that is private and one that’s public, without an underscore.

And in the init block you set LiveData to the value stored in SharedPreferences.

Now find toggleNightMode(). Replace the comment inside launch() with:

val darkThemeEnabled = _darkThemeEnabled.value!!
sharedPrefs.setDarkThemeEnabled(!darkThemeEnabled)
_darkThemeEnabled.value = !darkThemeEnabled

With this code, you find out if the dark theme is enabled. Then, you toggle that value by changing it and storing it in SharedPreferences. Finally, you set the current value to LiveData which you’ll observe in the Activity.

Note: You can use not-null assertion here because the value will always initialize in init.

This is a great opportunity for you to learn how to migrate data from SharedPreferences to Jetpack DataStore. Don’t be so impatient: You’ll do that soon. :]

For now, it’s time to observe and react to theme changes in CoursesActivity.

Observing Theme Changes

You implemented a dark/light mode toggle logic in CoursesViewModel.kt. Now it’s time to observe the changes and change the theme accordingly.

In learningcompanion/ui/view open CoursesActivity.kt. Find subscribeToData() and add the following at the bottom:

viewModel.darkThemeEnabled.observe(this) { nightModeActive ->
  this.nightModeActive = nightModeActive

  val defaultMode = if (nightModeActive) {
    AppCompatDelegate.MODE_NIGHT_YES 
  } else {
    AppCompatDelegate.MODE_NIGHT_NO
  }

  AppCompatDelegate.setDefaultNightMode(defaultMode)
}

In this code block you receive and process the darkThemeEnabled value by calling observe(this). Every time you tap the theme change icon, it changes and updates the value. Then, you use the value to set the theme accordingly, using AppCompatDelegate.setDefaultNightMode(defaultMode).

Build and run. Click the change theme icon and see your app go dark. Now close and reopen the app to confirm the theme persisted.

List of courses with the dark theme applied

This was a small introduction to set up the SharedPreferences functionality. With that set, it’s time to explore and migrate to the Jetpack DataStore.

Introducing Jetpack DataStore

As you learned in the introduction, Jetpack DataStore is a solution for data persistence that lets you store key-value pairs or typed objects by using protocol buffers. And it does it all asynchronously, using Kotlin Coroutines and Flow!

You can choose from two implementations:

  • Preferences DataStore uses keys to read and write data in a similar way to Shared Preferences.
  • Proto DataStore stores data as objects of a custom data type. When using Proto DataStore, you have to define a schema using protocol buffers.

But why would you switch from using SharedPreferences? Take a look at the differences between the Jetpack DataStore and SharedPreferences.

Comparing Jetpack DataStore and SharedPreferences

Almost every Android developer has used the simple and intuitive Shared Preferences API. It lets you quickly store and retrieve simple pieces of data. While useful, the truth is, it has several drawbacks.

The biggest drawbacks when using SharedPreferences include:

  • Lack of a fully asynchronous API
  • Lack of main thread safety
  • No type safety

Fortunately, Google built the Jetpack DataStore to address these issues. Because Flow powers it, Jetpack DataStore has an asynchronous API and main thread safety by default. All the work automatically moves to Dispatchers.IO under the hood, so you don’t have to worry about freezing your app while storing data.

Flow also provides safety from runtime exceptions and can signal errors. Later, you’ll see how easy it is to handle errors.

Note: For the full list of drawbacks and a more detailed comparison, check out this Android developers blog post.

Now it’s time to migrate your SharedPreferences to Preferences DataStore.