C1CTech

Android Jetpack Preferences DataStore Example

This blog is about Android Jetpack DataStore Preferences and how to implement DataStore Preferences in our Android application.

DataStore

Types of DataStore

DataStore provides two different types of implementations to store data.

Creating new project

  1. 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.
  2. Open project’s app level build.gradle file:
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
    jvmTarget = '1.8'
}
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")

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:

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:

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)
            }
        }
    }
}
Exit mobile version