Android Tutorial

Software Setup and Configuration

Android Studio

File Structure

Components

Core Topics

Layout

View

Button

Intent and Intent Filters

Toast

RecyclerView

Fragments

Adapters

Other UI Component

Image Loading Libraries

Date and Time

Material Design

Bars

Working with Google Maps

Chart

Animation

Database

Advance Android

Jetpack

Architecture

App Publish

App Monetization

Preferences DataStore in Android

DataStore is a modern data storage solution introduced by Google that comes as a replacement for SharedPreferences in Android. While SharedPreferences is a way to store key-value pairs, DataStore is more robust and offers more flexibility, such as type-safety and asynchronous operations using Kotlin coroutines.

There are two types of DataStore:

  1. Preferences DataStore: Used to store key-value pairs (similar to SharedPreferences).
  2. Proto DataStore: Uses a custom data type and serializes it as a protocol buffer.

Here, I'll show you how to use the Preferences DataStore:

1. Setup:

Add the required dependencies to your build.gradle:

implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"

Ensure you have the latest version.

2. Create an Instance:

You can create an instance of DataStore using the Context:

val dataStore: DataStore<Preferences> = context.createDataStore(
  name = "settings"
)

3. Read from DataStore:

Reading from the DataStore is asynchronous:

val EXAMPLE_KEY = preferencesKey<String>("example_key")

val exampleFlow: Flow<String?> = dataStore.data
  .map { preferences ->
      // Return the value or null if not set
      preferences[EXAMPLE_KEY]
  }

To observe this data in a UI, you can collect it:

lifecycleScope.launch {
  exampleFlow.collect { value ->
      // Use the value
  }
}

4. Write to DataStore:

Writing to DataStore is also asynchronous:

suspend fun saveString(key: Preferences.Key<String>, value: String) {
  dataStore.edit { preferences ->
      preferences[key] = value
  }
}

You'd call this function from a coroutine:

lifecycleScope.launch {
  saveString(EXAMPLE_KEY, "This is an example")
}

5. Migrating from SharedPreferences:

DataStore also provides utilities to migrate from SharedPreferences:

val dataStore: DataStore<Preferences> = context.createDataStore(
  name = "settings",
  migrations = listOf(
      SharedPreferencesMigration(context, "legacy_preferences_name")
  )
)

Benefits over SharedPreferences:

  1. Type Safety: DataStore provides a more type-safe API compared to SharedPreferences.
  2. Asynchronous: All DataStore operations are asynchronous.
  3. More Robust: DataStore uses Kotlin coroutines to handle data consistency during concurrent updates.
  4. Protocol Buffers: With Proto DataStore, you can leverage the benefits of protocol buffers, which are efficient and structured.

Remember, as with any new library, always refer to the official documentation to understand the full capabilities and best practices.

  1. Preferences DataStore example code Android:

    // Create a DataStore instance
    val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")
    
    // Write data to DataStore
    dataStore.edit { preferences ->
        preferences[KEY_NAME] = "John Doe"
        preferences[KEY_AGE] = 25
    }
    
    // Read data from DataStore
    val nameFlow: Flow<String?> = dataStore.data.map { preferences ->
        preferences[KEY_NAME] ?: ""
    }
    
    // Observe changes in data
    nameFlow.collect { name ->
        Log.d(TAG, "Name: $name")
    }
    
  2. Migrating from SharedPreferences to DataStore in Android:

    Update your code by replacing SharedPreferences with DataStore. Use the edit function to write data and observe changes using Flow.

  3. Working with key-value pairs in Preferences DataStore:

    Use key-value pairs for storing and retrieving data:

    // Write data
    dataStore.edit { preferences ->
        preferences[KEY_USER_ID] = "123"
    }
    
    // Read data
    val userId: Flow<String?> = dataStore.data.map { preferences ->
        preferences[KEY_USER_ID] ?: ""
    }
    
  4. Custom data types in Preferences DataStore Android:

    Serialize and deserialize custom data types:

    @Serializable
    data class UserProfile(val name: String, val age: Int)
    
    // Write custom data
    dataStore.edit { preferences ->
        preferences[KEY_USER_PROFILE] = UserProfile("John Doe", 25)
    }
    
    // Read custom data
    val userProfile: Flow<UserProfile?> = dataStore.data.map { preferences ->
        preferences[KEY_USER_PROFILE]
    }
    
  5. Reading and writing data with Preferences DataStore:

    Use the edit function to write data and access data using the data property. Combine with map for transformations.

  6. Preferences DataStore and Jetpack Navigation in Android:

    Access DataStore in different fragments or activities. Share the same DataStore instance to maintain consistency across the app.

  7. Preferences DataStore and Kotlin Coroutines:

    Use coroutines to perform asynchronous operations with DataStore. Wrap data access operations with withContext(Dispatchers.IO).

  8. Preferences DataStore with encrypted preferences in Android:

    Use Android's EncryptedSharedPreferences or consider encrypting the data before storing it in DataStore for enhanced security.

  9. Preferences DataStore and LiveData integration:

    Convert Flow to LiveData for easier integration with UI components:

    val userIdLiveData: LiveData<String?> = userId.asLiveData()
    
  10. Handling data changes with Preferences DataStore:

    Use the collect function on the Flow to observe changes in the data:

    nameFlow.collect { name ->
        Log.d(TAG, "Name: $name")
    }