This blog is about Android Jetpack DataStore Preferences and how to implement DataStore Preferences in our Android application.
DataStore
- Jetpack DataStore is a data storage solution.
- It allows us to store key-value pairs (like SharedPreferences) or typed objects with protocol buffers (discuss in the next article).
- DataStore uses Kotlin, Coroutines and Flow to store data asynchronously with consistency and transaction support.
- DataStore is the new data storage solution which is the replacement of SharedPreferences.
Types of DataStore
DataStore provides two different types of implementations to store data.
- Preferences 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 (discuss in the next article).
Creating new project
- Create a new project by going to File ⇒ New Android Project, select Empty Activity , provide app name, select language to kotlin and then finally click on finish.
- Open project’s app level build.gradle file:
- Go to the android block and add the below lines.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
- Now add the below dependencies inside dependencies block. Once you have added all these, sync your project.
dependencies {
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha07"
// Lifecycle component
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
// Kotlin coroutines components
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
}
3 . Open your activity_main.xml. To create the following UI, add the below code.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_60dp"
android:background="@drawable/background"
android:padding="@dimen/padding_18dp"
android:text="Save user"
android:textColor="@android:color/white"
android:textSize="@dimen/textsize_15sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/switch_gender"
app:layout_constraintStart_toStartOf="@+id/switch_gender"
app:layout_constraintTop_toBottomOf="@+id/switch_gender" />
<EditText
android:id="@+id/et_lname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:ems="10"
android:hint="@string/hint_enter_last_name"
app:layout_constraintEnd_toEndOf="@+id/et_fname"
app:layout_constraintStart_toStartOf="@+id/et_fname"
app:layout_constraintTop_toBottomOf="@+id/et_fname" />
<EditText
android:id="@+id/et_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:ems="10"
android:hint="@string/hint_enter_age"
android:inputType="number"
app:layout_constraintEnd_toEndOf="@+id/et_lname"
app:layout_constraintStart_toStartOf="@+id/et_lname"
app:layout_constraintTop_toBottomOf="@+id/et_lname"
tools:layout_editor_absoluteY="317dp" />
<EditText
android:id="@+id/et_fname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_30dp"
android:ems="10"
android:hint="@string/hint_enter_first_name"
app:layout_constraintEnd_toEndOf="@+id/tv_gender"
app:layout_constraintStart_toStartOf="@+id/tv_gender"
app:layout_constraintTop_toBottomOf="@+id/tv_gender"
tools:layout_editor_absoluteY="178dp" />
<TextView
android:id="@+id/tv_fname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_30dp"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="@dimen/textsize_20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_lname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="@dimen/textsize_20sp"
app:layout_constraintEnd_toEndOf="@+id/tv_fname"
app:layout_constraintStart_toStartOf="@+id/tv_fname"
app:layout_constraintTop_toBottomOf="@+id/tv_fname" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="@dimen/textsize_20sp"
app:layout_constraintEnd_toEndOf="@+id/tv_lname"
app:layout_constraintStart_toStartOf="@+id/tv_lname"
app:layout_constraintTop_toBottomOf="@+id/tv_lname" />
<TextView
android:id="@+id/tv_gender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="@dimen/textsize_20sp"
app:layout_constraintEnd_toEndOf="@+id/tv_age"
app:layout_constraintStart_toStartOf="@+id/tv_age"
app:layout_constraintTop_toBottomOf="@+id/tv_age" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_gender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16dp"
android:text="@string/female_male"
android:textSize="@dimen/textsize_20sp"
app:layout_constraintEnd_toEndOf="@+id/et_age"
app:layout_constraintStart_toStartOf="@+id/et_age"
app:layout_constraintTop_toBottomOf="@+id/et_age" />
</androidx.constraintlayout.widget.ConstraintLayout>
Creating a Preferences DataStore
4. Create a new kotlin file User.kt and add the below code.
User.kt
package com.c1ctech.jetpackdatastore
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
// At the top level of your kotlin file
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")
- dataStore: instance of Datastore<Preferences> created by preferencesDataStore.
- name parameter(mandatory) : is the name of the Preferences DataStore.
5 .Create a new kotlin class UserManager.kt and add the below code.
UserManager.kt
package com.c1ctech.jetpackdatastore
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class UserManager(val dataStore: DataStore<Preferences>) {
//Create some keys
companion object {
val USER_AGE_KEY = intPreferencesKey("USER_AGE")
val USER_FIRST_NAME_KEY = stringPreferencesKey("USER_FIRST_NAME")
val USER_LAST_NAME_KEY = stringPreferencesKey("USER_LAST_NAME")
val USER_GENDER_KEY = booleanPreferencesKey("USER_GENDER")
}
//Store user data
suspend fun storeUser(age: Int, fname: String, lname: String, isMale: Boolean) {
dataStore.edit {
it[USER_AGE_KEY] = age
it[USER_FIRST_NAME_KEY] = fname
it[USER_LAST_NAME_KEY] = lname
it[USER_GENDER_KEY] = isMale
}
}
//Create an age flow
val userAgeFlow: Flow<Int?> = dataStore.data.map {
it[USER_AGE_KEY]
}
//Create a fname flow
val userFirstNameFlow: Flow<String?> = dataStore.data.map {
it[USER_FIRST_NAME_KEY]
}
//Create a lname flow
val userLastNameFlow: Flow<String?> = dataStore.data.map {
it[USER_LAST_NAME_KEY]
}
//Create a gender flow
val userGenderFlow: Flow<Boolean?> = dataStore.data.map {
it[USER_GENDER_KEY]
}
}
Defining Key for the Value that needs to be saved:
- To define a key for each value that you need to store in the DataStore<Preferences> instance, you must use the corresponding key type function. For example:
val USER_AGE_KEY = intPreferencesKey("USER_AGE")
val USER_FIRST_NAME_KEY = stringPreferencesKey("USER_FIRST_NAME")
val USER_LAST_NAME_KEY = stringPreferencesKey("USER_LAST_NAME")
val USER_GENDER_KEY = booleanPreferencesKey("USER_GENDER")
Saving Value to Preference DataStore:
Reading Value back from Preference DataStore:
- To read the value from a Preferences DataStore we need to use the DataStore.data property which returns a Flow<Preferences> and with the .map{} operator we can can get the Flow<> (value) by passing the right key into the preferences.
6 . Open MainActivity.kt and add the below code.
MainActivity.kt
package com.c1ctech.jetpackdatastore
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.asLiveData
import androidx.lifecycle.observe
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
lateinit var userManager: UserManager
var age = 0
var fname = ""
var lname = ""
var gender = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Get reference to our userManager class
userManager = UserManager(dataStore)
buttonSave()
observeData()
}
private fun observeData() {
//Updates age
userManager.userAgeFlow.asLiveData().observe(this, {
if (it != null) {
age = it
tv_age.text = it.toString()
}
})
//Updates firstname
userManager.userFirstNameFlow.asLiveData().observe(this, {
if (it != null) {
fname = it
tv_fname.text = it
}
})
//Updates lastname
userManager.userLastNameFlow.asLiveData().observe(this, {
if (it != null) {
lname = it
tv_lname.text = it
}
})
//Updates gender
userManager.userGenderFlow.asLiveData().observe(this, {
if (it != null) {
gender = if (it) "Male" else "Female"
tv_gender.text = gender
}
})
}
private fun buttonSave() {
//Gets the user input and saves it
btn_save.setOnClickListener {
fname = et_fname.text.toString()
lname = et_lname.text.toString()
age = et_age.text.toString().toInt()
val isMale = switch_gender.isChecked
//Stores the values
GlobalScope.launch {
userManager.storeUser(age, fname, lname, isMale)
}
}
}
}
- In order to get the saved values in the activity from the DataStore, we need to change the Flow<> to LiveData with .asLiveData() and observe it.
