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ć.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
DataStore Tutorial For Android: Getting Started
30 mins
- Getting Started
- Enabling Auto Import
- Implementing Theme Change
- Observing Theme Changes
- Introducing Jetpack DataStore
- Comparing Jetpack DataStore and SharedPreferences
- Migrating SharedPreferences to Preferences DataStore
- Creating an Abstraction for Prefs DataStore
- Creating Prefs DataStore
- Reading Data From Prefs DataStore
- Observing Values From Prefs DataStore
- Writing Data to DataStore
- Introducing Proto DataStore
- Preparing Gradle for Proto DataStore
- Creating Proto Files
- Defining Proto Objects
- Creating a Serializer
- Preparing ProtoStore
- Creating Proto DataStore
- Storing Filter Options
- Reading Filter Options
- Reacting To Filter Changes
- Where to Go From Here?
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.
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:
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.
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 Studio ▸ Preferences. On a PC, go to File ▸ Settings. Then go to Editor ▸ General ▸ Auto 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.
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
.
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.
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.
Now it’s time to migrate your SharedPreferences
to Preferences DataStore.