DataStore Part of Android Jetpack.
Jetpack DataStore is a data storage solution that allows you to store key-value pairs or typed objects with protocol buffers. DataStore uses Kotlin coroutines and Flow to store data asynchronously, consistently, and transactionally.
If you're currently using
SharedPreferences to
store data, consider migrating to DataStore instead.
Preferences DataStore and Proto DataStore
DataStore provides two different implementations: Preferences DataStore and Proto DataStore.
- Preferences DataStore stores and accesses data using keys. This implementation does not require a predefined schema, and it does not provide type safety.
- Proto DataStore stores data as instances of a custom data type. This implementation requires you to define a schema using protocol buffers, but it provides type safety.
Setup
To use Jetpack DataStore in your app, add the following to your Gradle file depending on which implementation you want to use:
dependencies {
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"
// Proto DataStore
implementation "androidx.datastore:datastore-core:1.0.0-alpha01"
}
Store key-value pairs with Preferences DataStore
The Preferences DataStore implementation uses the
DataStore and
Preferences
classes to persist simple key-value pairs to disk.
Create a Preferences DataStore
Use the
Context.createDataStore()
extension function to create an instance of DataStore<Preferences>. The
mandatory name parameter is the name of the Preferences DataStore.
val dataStore: DataStore<Preferences> = context.createDataStore(
name = "settings"
)
Read from a Preferences DataStore
Because Preferences DataStore does not use a predefined schema, you must use
preferencesKey()
to define a key for each value that you need to store in the
DataStore<Preferences> instance. Then, use the
DataStore.data property
to expose the appropriate stored value using a Flow.
val EXAMPLE_COUNTER = preferencesKey<Int>("example_counter")
val exampleCounterFlow: Flow<Int> = dataStore.data
.map { preferences ->
// No type safety.
preferences[EXAMPLE_COUNTER] ?: 0
}
Write to a Preferences DataStore
Preferences DataStore provides an
edit()
function that transactionally updates the data in a DataStore. The function's
transform parameter accepts a block of code where you can update the values as
needed. All of the code in the transform block is treated as a single
transaction.
suspend fun incrementCounter() {
dataStore.edit { settings ->
val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
settings[EXAMPLE_COUNTER] = currentCounterValue + 1
}
}
Store typed objects with Proto DataStore
The Proto DataStore implementation uses DataStore and protocol buffers to persist typed objects to disk.
Define a schema
Proto DataStore requires a predefined schema in a proto file in the
app/src/main/proto/ directory. This schema defines the type for the objects
that you persist in your Proto DataStore. To learn more about defining a proto
schema, see the protobuf language
guide.
syntax = "proto3";
option java_package = "com.example.application";
option java_multiple_files = true;
message Settings {
int example_counter = 1;
}
Create a Proto DataStore
There are two steps involved in creating a Proto DataStore to store your typed objects:
- Define a class that implements
Serializer<T>, whereTis the type defined in the proto file. This serializer class tells DataStore how to read and write your data type. - Use the
Context.createDataStore()extension function to create an instance ofDataStore<T>, whereTis the type defined in the proto file. Thefilenameparameter tells DataStore which file to use to store the data, and theserializerparameter tells DataStore the name of the serializer class defined in step 1.
object SettingsSerializer : Serializer<Settings> {
override fun readFrom(input: InputStream): Settings {
try {
return Settings.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
override fun writeTo(
t: Settings,
output: OutputStream) = t.writeTo(output)
}
val settingsDataStore: DataStore<Settings> = context.createDataStore(
fileName = "settings.pb",
serializer = SettingsSerializer
)
Read from a Proto DataStore
Use DataStore.data to expose a Flow of the appropriate property from your stored object.
val exampleCounterFlow: Flow<Int> = settingsDataStore.data
.map { settings ->
// The exampleCounter property is generated from the proto schema.
settings.exampleCounter
}
Write to a Proto DataStore
Proto DataStore provides an
updateData()
function that transactionally updates a stored object. updateData() gives you
the current state of the data as an instance of your data type and updates the
data transactionally in an atomic read-write-modify operation.
suspend fun incrementCounter() {
settingsDataStore.updateData { currentSettings ->
currentSettings.toBuilder()
.setExampleCounter(currentSettings.exampleCounter + 1)
.build()
}
}
Use DataStore in synchronous code
One of the primary benefits of DataStore is the asynchronous API, but it may not always be feasible to change your surrounding code to be asynchronous. This might be the case if you're working with an existing codebase that uses synchronous disk I/O or if you have a dependency that doesn't provide an asynchronous API.
Kotlin coroutines provide the
runBlocking()
coroutine builder to help bridge the gap between synchronous and asynchronous
code. You can use runBlocking() to read data from DataStore synchronously. The
following code blocks the calling thread until DataStore returns data:
val exampleData = runBlocking { dataStore.data.first() }
Performing synchronous I/O operations on the UI thread can cause ANRs or UI jank. You can mitigate these issues by asynchronously preloading the data from DataStore:
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
dataStore.data.first()
// You should also handle IOExceptions here.
}
}
This way, DataStore asynchronously reads the data and caches it in memory. Later
synchronous reads using runBlocking() may be faster or may avoid a disk I/O
operation altogether if the initial read has completed.
Provide feedback
Share your feedback and ideas with us through these resources:
- Issue tracker

- Report issues so we can fix bugs.
Additional resources
To learn more about Jetpack DataStore, see the following additional resources:

