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.