If you’re still using SharedPreferences in your Android app, it’s time to move forward. Google introduced Jetpack DataStore as a modern, efficient, and fully asynchronous solution for storing key-value pairs and typed objects. In this blog, we’ll break down what Jetpack DataStore is, why it’s better than SharedPreferences, and how you can use it effectively in your Android projects.
What Is Jetpack DataStore?
Jetpack DataStore is part of Android Jetpack and is designed to store small amounts of data. It comes in two flavors:
- Preferences DataStore — stores key-value pairs, similar to SharedPreferences.
- Proto DataStore — stores typed objects using Protocol Buffers.
Unlike SharedPreferences, Jetpack DataStore is built on Kotlin coroutines and Flow, making it asynchronous and safe from potential ANRs (Application Not Responding errors).
Why Replace SharedPreferences?
SharedPreferences has been around for a long time but comes with some baggage:
- Synchronous API — can block the main thread.
- Lacks error handling — fails silently.
- Not type-safe — you can run into ClassCastExceptions easily.
Jetpack DataStore solves all of these with:
- Coroutine support for non-blocking IO.
- Strong typing with Proto DataStore.
- Built-in error handling.
- Better consistency and reliability.
Setting Up Jetpack DataStore
To start using Jetpack DataStore, first add the required dependencies to your build.gradle:
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.datastore:datastore-core:1.0.0"For Proto DataStore:
implementation "androidx.datastore:datastore:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.14.0"Also, don’t forget to apply the protobuf plugin if using Proto:
id 'com.google.protobuf' version '0.8.12'Using Preferences DataStore
Step 1: Create the DataStore instance
Jetpack DataStore is designed to be singleton-scoped. The recommended way is to create it as an extension property on Context:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")Here, preferencesDataStore creates a singleton DataStore instance. This ensures you have a single DataStore instance per file, avoiding memory leaks and data corruption.
Step 2: Define keys
val USER_NAME = stringPreferencesKey("user_name")
val IS_LOGGED_IN = booleanPreferencesKey("is_logged_in")stringPreferencesKey and booleanPreferencesKey help define the keys.
Step 3: Write data
To write data, use the edit function, which is fully asynchronous and safe to call from any thread:
suspend fun saveUserData(context: Context, name: String, isLoggedIn: Boolean) {
context.dataStore.edit { preferences ->
preferences[USER_NAME] = name
preferences[IS_LOGGED_IN] = isLoggedIn
}
}Here, edit suspends while the data is being written, ensuring no UI thread blocking.
Step 4: Read data
To read data, use Kotlin Flows, which emit updates whenever the data changes:
val userNameFlow: Flow<String> = context.dataStore.data
.map { preferences ->
preferences[USER_NAME] ?: ""
}Here, data is accessed reactively using Kotlin Flow, returns a Flow<String> that emits the username whenever it changes. You can collect this Flow in a coroutine or observe it in Jetpack Compose.
Real-World Use Case: User Login State
Let’s say you want to keep track of whether a user is logged in. Here’s how you do it:
Save login state:
suspend fun setLoginState(context: Context, isLoggedIn: Boolean) {
context.dataStore.edit { prefs ->
prefs[IS_LOGGED_IN] = isLoggedIn
}
}Observe login state:
val loginState: Flow<Boolean> = context.dataStore.data
.map { prefs -> prefs[IS_LOGGED_IN] ?: false }This setup lets your app reactively respond to changes in the login state, such as redirecting users to the login screen or the home screen.
Migrating from SharedPreferences
Jetpack DataStore makes migration easy with SharedPreferencesMigration:
import androidx.datastore.preferences.SharedPreferencesMigration
val Context.dataStore by preferencesDataStore(
name = USER_PREFERENCES_NAME,
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, USER_PREFERENCES_NAME))
}
)- Migration runs automatically before any DataStore access.
- Once migrated, stop using the old SharedPreferences to avoid data inconsistency.
Using Proto DataStore (Typed Data)
Proto DataStore requires you to define a .proto schema file.
Step 1: Define the Proto schema
user_prefs.proto
syntax = "proto3";
option java_package = "com.softaai.sitless";
option java_multiple_files = true;
message UserPreferences {
string user_name = 1;
bool is_logged_in = 2;
}Step 2: Create the serializer
object UserPreferencesSerializer : Serializer<UserPreferences> {
override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()
override suspend fun readFrom(input: InputStream): UserPreferences {
return UserPreferences.parseFrom(input)
}
override suspend fun writeTo(t: UserPreferences, output: OutputStream) {
t.writeTo(output)
}
}Step 3: Initialize Proto DataStore
val Context.userPreferencesStore: DataStore<UserPreferences> by dataStore(
fileName = "user_prefs.pb",
serializer = UserPreferencesSerializer
)Step 4: Update and read data
suspend fun updateUser(context: Context, name: String, isLoggedIn: Boolean) {
context.userPreferencesStore.updateData { prefs ->
prefs.toBuilder()
.setUserName(name)
.setIsLoggedIn(isLoggedIn)
.build()
}
}
val userNameFlow = context.userPreferencesStore.data
.map { it.userName }Best Practices
- Use Proto DataStore when your data model is complex or needs strong typing.
- Use Preferences DataStore for simple key-value storage.
- Always handle exceptions using
catchwhen collecting flows. - Avoid main-thread operations; DataStore is built for background execution.
Conclusion
Jetpack DataStore is not just a replacement for SharedPreferences; it’s an upgrade in every sense. With better performance, safety, and modern API design, it’s the future of local data storage in Android.
If you’re building a new Android app or refactoring an old one, now’s the perfect time to switch. By embracing Jetpack DataStore, you’re not only writing cleaner and safer code, but also aligning with best practices endorsed by Google.
