— Android Development, DataStore, SharedPreferences, Migration — 1 min read
DataStore is a modern data storage solution provided by Jetpack. It replaces SharedPreferences and offers several advantages, including type safety, asynchronous operations using Kotlin Coroutines and Flow, and better error handling. There are two types of DataStore: Preferences DataStore (for simple key-value pairs) and Proto DataStore (for more complex data structures).
In this article, we'll focus on Preferences DataStore.
Before we proceed, make sure you have the following prerequisites:
Add Dependencies:
First, add the following dependency to your build.gradle file:
1implementation "androidx.datastore:datastore-preferences:1.1.0"Note that you might need to set the Kotlin target version to 1.8 in your kotlinOptions as well.
Create DataStore Instance:
Initialize your DataStore instance by specifying the name (similar to how you used to create SharedPreferences):
1private val dataStore: DataStore<Preferences> by preferencesDataStore(name = "file_name")Migrate Key-Value Pairs:
Identify the keys you want to migrate from SharedPreferences. Specify these keys in the keysToMigrate parameter when creating a SharedPreferencesMigration. For example:
1private val dataStore: DataStore<Preferences> by preferencesDataStore(2 name = "file_name",3 produceMigrations = { context ->4 listOf(5 SharedPreferencesMigration(6 context = context,7 sharedPreferencesName = "shared_prefs_name",8 keysToMigrate = setOf("example_key")9 )10 )11 }12)Reading Data from DataStore:
Unlike SharedPreferences, reading data from DataStore is asynchronous using Kotlin Flow. Handle exceptions (such as IOException) appropriately:
1val userPreferencesFlow: Flow<String> = dataStore.data2 .catch { exception ->3 if (exception is IOException) {4 emit(emptyPreferences())5 } else {6 throw exception7 }8 }9 .map { preferences ->10 preferences[PreferencesKeys.EXAMPLE_KEY] ?: "defaultValue"11 }Avoid Blocking the UI Thread:
Be cautious when using runBlocking for synchronous operations. It can lead to ANRs (Application Not Responding) on the UI thread. Instead, use suspend functions and handle data retrieval asynchronously.
1suspend fun getExampleKey(): String = withContext(ioDispatcher) {2 dataStore.data.map { preferences ->3 preferences[EXAMPLE_KEY] ?: "defaultValue"4 }.first()5}By migrating to DataStore, you'll enjoy better performance, type safety, and improved error handling. Remember that DataStore is not suitable for large-scale data storage or partial updates. For those scenarios, consider using Room.