Android & Kotlin

Mastering Datastore in Android: A Guide for Developers

Pinterest LinkedIn Tumblr

Are you an Android developer looking to level up your skills in managing data within your applications? Look no further! In this comprehensive guide, we will take you on a journey to mastering Datastore in Android.

Datastore is a powerful and flexible solution provided by Google that allows developers to efficiently store and sync structured data in their apps. Whether you’re a beginner or an experienced developer, this guide will equip you with the knowledge and skills needed to effectively work with Datastore and unleash its full potential.

What is DataStore in Android

DataStore is a new data storage solution introduced by Google as part of the Android Jetpack library. It is designed to simplify the process of storing and managing data in your Android app, providing a modern, efficient, and easy-to-use alternative to SharedPreferences.

DataStore offers two implementations:

  1. Preferences DataStore: This implementation is similar to SharedPreferences but provides better performance and supports both synchronous and asynchronous data read and write operations.
  2. Proto DataStore: This implementation uses Protocol Buffers (protobuf) to serialize data. It is more powerful and flexible than Preferences DataStore, as it allows you to define complex data structures and handle versioning more effectively.

Key features and benefits of DataStore:

  1. Type-Safe: DataStore uses Kotlin’s type-safe APIs, reducing the risk of runtime errors.
  2. Asynchronous API: DataStore provides coroutines-based asynchronous read and write operations, which are essential for not blocking the UI thread.
  3. Performance: DataStore is designed for efficiency and offers better performance than SharedPreferences, especially when handling large amounts of data or complex data structures.
  4. Consistency and Atomicity: DataStore ensures that write operations are consistent and atomic, preventing data corruption or loss.
  5. Compatibility: DataStore can be used on devices running Android 5.0 (API level 21) and higher.

How to use DataStore

To provide you with a complete example of using DataStore in Android, let’s create a simple app that stores and retrieves user settings using Preferences DataStore. We’ll build an app that allows the user to set and retrieve a username and userId.

1. Set up your Android project and add the DataStore dependency in the build.gradle file:

dependencies {
    implementation "androidx.datastore:datastore-preferences:1.0.0"
}

2. Create a data class to hold the user pieces of information such as username, age, token, etc.

package com.preferencesdatastore.data

data class UserSettings(val username: String, val userId: String)


3. Create a DataStoreManager

package com.preferencesdatastore.data


import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException

class DataStoreManager(private val context: Context) {
  private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_settings")

  val userSettingsFlow: Flow<UserSettings> = context.dataStore.data.catch { exception ->
    // Handle IOException
    if (exception is IOException) {
      emit(emptyPreferences())
    } else {
      throw exception
    }
  }.map { preferences ->
    // Retrieve the username from preferences
    val username = preferences[KEY_USERNAME] ?: ""
    val accessToken = preferences[KEY_ID] ?: ""
    UserSettings(username, accessToken)
  }

  suspend fun saveUserSettings(username: String, accessToken: String) {
    context.dataStore.edit { preferences ->
      preferences[KEY_USERNAME] = username
      preferences[KEY_ID] = accessToken
    }
  }

  suspend fun clearUserSettings() {
    context.dataStore.edit { preferences ->
      preferences[KEY_USERNAME] = ""
      preferences[KEY_ID] = ""
    }
  }


  companion object {
    private val KEY_USERNAME = stringPreferencesKey("key_username")
    private val KEY_ID = stringPreferencesKey("key_user_id")
  }
}

4. Create UserSettingsScreen for taking input from user

package com.preferencesdatastore

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.preferencesdatastore.data.DataStoreManager
import kotlinx.coroutines.launch

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserSettingsScreen(dataStoreManager: DataStoreManager) {
  var username by remember { mutableStateOf("") }
  var token by remember { mutableStateOf("") }
  val scope = rememberCoroutineScope()

  Column(
    modifier = Modifier.padding(16.dp)
  ) {
    Text(
      text = "DataStore Tutorials", style = MaterialTheme.typography.titleLarge
    )
    Spacer(modifier = Modifier.height(8.dp))
    OutlinedTextField(
      value = username,
      onValueChange = { username = it },
      label = { Text("Enter Username") },
      modifier = Modifier.fillMaxWidth()
    )
    Spacer(modifier = Modifier.height(8.dp))
    OutlinedTextField(
      value = token,
      onValueChange = { token = it },
      label = { Text("Enter UserId") },
      modifier = Modifier.fillMaxWidth()
    )

    Spacer(modifier = Modifier.height(16.dp))
    Row(modifier = Modifier.align(Alignment.End)) {
      Button(onClick = {
        scope.launch {
          dataStoreManager.clearUserSettings()
        }
      }) {
        Text("Clear")
      }
      Spacer(modifier = Modifier.width(8.dp))
      Button(onClick = {
        scope.launch {
          dataStoreManager.saveUserSettings(username, token)
        }
      }) {
        Text("Save")
      }
    }
  }

}


5. Replace the contents of MainActivity.kt with the following code:

package com.preferencesdatastore

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.preferencesdatastore.data.DataStoreManager
import com.preferencesdatastore.data.UserSettings
import com.preferencesdatastore.ui.theme.PreferencesDataStoreExampleTheme
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

  private lateinit var dataStoreManager: DataStoreManager

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    dataStoreManager = DataStoreManager(this)

    setContent {
      PreferencesDataStoreExampleTheme {
        // A surface container using the 'background' color from the theme
        Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
          Column(
            modifier = Modifier
              .fillMaxSize()
              .padding(top = 56.dp)
          ) {
            UserSettingsScreen(dataStoreManager = dataStoreManager)
            var userSettings by remember { mutableStateOf(UserSettings("", "")) }
            val scope = rememberCoroutineScope()
            LaunchedEffect(Unit) {
              scope.launch {
                dataStoreManager.userSettingsFlow.collectLatest { newUserSettings ->
                  userSettings = newUserSettings
                }
              }
            }
            Text(
              modifier = Modifier.padding(16.dp),
              text = "Username: ${userSettings.username} \nUserId: ${userSettings.userId}"
            )
          }
        }
      }
    }
  }
}

This code sets up a simple Android Compose UI to capture a username and userId from the user, save the data using DataStore Preferences, and then display the saved data in a TextView. When the “Save Data” button is clicked, the values entered by the user will be saved using DataStore Preferences. The saved data is then displayed on the screen below the button.

Write A Comment