— 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
.