# DataStore
Part of [Android Jetpack](https://developer.android.com/jetpack).


Try with Kotlin Multiplatform  
Kotlin Multiplatform allows sharing the data layer with other platforms. Learn how to set up and work with DataStore in KMP  
[Set up DataStore for KMP →](https://developer.android.com/kotlin/multiplatform/datastore)  
![](https://developer.android.com/static/images/android-kmp-logo.png)

<br />

Jetpack DataStore is a data storage solution that allows you to store key-value
pairs or typed objects with [protocol
buffers](https://developers.google.com/protocol-buffers). DataStore uses Kotlin
coroutines and Flow to store data asynchronously, consistently, and
transactionally.

If you're currently using
[`SharedPreferences`](https://developer.android.com/reference/kotlin/android/content/SharedPreferences) to
store data, consider migrating to DataStore instead.
| **Note:** If you need to support large or complex datasets, partial updates, or referential integrity, consider using [Room](https://developer.android.com/training/data-storage/room) instead of DataStore. DataStore is ideal for small, simple datasets and does not support partial updates or referential integrity.

## 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](https://developers.google.com/protocol-buffers), but it provides type safety.

## Using DataStore correctly

In order to use DataStore correctly always keep in mind the following rules:

1. **Never create more than one instance of `DataStore` for a given file in
   the same process.** Doing so can break all DataStore functionality. If there are
   multiple DataStores active for a given file in the same process, DataStore will
   throw `IllegalStateException` when reading or updating data.

2. **The generic type of the `DataStore<T>` must be immutable.** Mutating a type
   used in DataStore invalidates any guarantees that DataStore provides and creates
   potentially serious, hard-to-catch bugs. It is strongly recommended that you use
   protocol buffers which provide immutability guarantees, a simple API, and
   efficient serialization.

3. **Never mix usages of `SingleProcessDataStore` and `MultiProcessDataStore`**
   for the same file. If you intend to access the `DataStore` from more than one
   process, always use [`MultiProcessDataStore`](https://developer.android.com/topic/libraries/architecture/datastore#multiprocess).

## Setup

To use Jetpack DataStore in your app, add the following to your Gradle file
depending on which implementation you want to use:

### Preferences DataStore

### Groovy

```groovy
    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation "androidx.datastore:datastore-preferences:1.1.7"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-preferences-rxjava3:1.1.7"
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation "androidx.datastore:datastore-preferences-core:1.1.7"
    }
    
```

### Kotlin

```kotlin
    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation("androidx.datastore:datastore-preferences:1.1.7")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-preferences-rxjava2:1.1.7")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-preferences-core:1.1.7")
    }
    
```

### Proto DataStore

### Groovy

```groovy
    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation "androidx.datastore:datastore:1.1.7"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-rxjava3:1.1.7"
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation "androidx.datastore:datastore-core:1.1.7"
    }
    
```

### Kotlin

```kotlin
    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation("androidx.datastore:datastore:1.1.7")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-rxjava2:1.1.7")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-rxjava3:1.1.7")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-core:1.1.7")
    }
    
```

## Store key-value pairs with Preferences DataStore

The Preferences DataStore implementation uses the
[`DataStore`](https://developer.android.com/reference/kotlin/androidx/datastore/core/DataStore) and
[`Preferences`](https://developer.android.com/reference/kotlin/androidx/datastore/preferences/core/Preferences)
classes to persist simple key-value pairs to disk.

### Create a Preferences DataStore

Use the property delegate created by [`preferencesDataStore`](https://developer.android.com/reference/kotlin/androidx/datastore/preferences/package-summary#dataStore) to create an instance of `DataStore<Preferences>`. Call it once at the top level of your kotlin file, and access it through this property throughout the rest of your application. This makes it easier to keep your `DataStore` as a singleton. Alternatively, use [`RxPreferenceDataStoreBuilder`](https://developer.android.com/reference/kotlin/androidx/datastore/rxjava2/RxDataStoreBuilder)
if you're using RxJava. The mandatory `name` parameter is the name of the
Preferences DataStore.  

### Kotlin

```kotlin
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
```

### Java

```java
RxDataStore<Preferences> dataStore =
  new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();
```

### Read from a Preferences DataStore

Because Preferences DataStore does not use a predefined schema, you must use the
corresponding key type function to define a key for each value that you need to
store in the `DataStore<Preferences>` instance. For example, to define a key
for an int value, use
[`intPreferencesKey()`](https://developer.android.com/reference/kotlin/androidx/datastore/preferences/core/package-summary#intPreferencesKey(kotlin.String)).
Then, use the
[`DataStore.data`](https://developer.android.com/reference/kotlin/androidx/datastore/core/DataStore#data) property
to expose the appropriate stored value using a `Flow`.  

### Kotlin

```kotlin
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = context.dataStore.data
  .map { preferences ->
    // No type safety.
    preferences[EXAMPLE_COUNTER] ?: 0
}
```

### Java

```java
Preferences.Key<Integer> EXAMPLE_COUNTER = PreferencesKeys.int("example_counter");

Flowable<Integer> exampleCounterFlow =
  dataStore.data().map(prefs -> prefs.get(EXAMPLE_COUNTER));
```

### Write to a Preferences DataStore

Preferences DataStore provides an
[`edit()`](https://developer.android.com/reference/kotlin/androidx/datastore/preferences/core/package-summary#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.  

### Kotlin

```kotlin
suspend fun incrementCounter() {
  context.dataStore.edit { settings ->
    val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
    settings[EXAMPLE_COUNTER] = currentCounterValue + 1
  }
}
```

### Java

```java
Single<Preferences> updateResult =  dataStore.updateDataAsync(prefsIn -> {
  MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
  Integer currentInt = prefsIn.get(INTEGER_KEY);
  mutablePreferences.set(INTEGER_KEY, currentInt != null ? currentInt + 1 : 1);
  return Single.just(mutablePreferences);
});
// The update is completed once updateResult is completed.
```

## Store typed objects with Proto DataStore

The Proto DataStore implementation uses DataStore and [protocol
buffers](https://developers.google.com/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](https://developers.google.com/protocol-buffers/docs/proto3).  

    syntax = "proto3";

    option java_package = "com.example.application.proto";
    option java_multiple_files = true;

    message Settings {
      int32 example_counter = 1;
    }

| **Note:** The class for your stored objects is generated at compile time from the `message` defined in the proto file. Make sure you rebuild your project.

### Create a Proto DataStore

There are two steps involved in creating a Proto DataStore to store your typed
objects:

1. Define a class that implements `Serializer<T>`, where `T` is the type defined in the proto file. This serializer class tells DataStore how to read and write your data type. Make sure you include a default value for the serializer to be used if there is no file created yet.
2. Use the property delegate created by `dataStore` to create an instance of `DataStore<T>`, where `T` is the type defined in the proto file. Call this once at the top level of your kotlin file and access it through this property delegate throughout the rest of your app. The `filename` parameter tells DataStore which file to use to store the data, and the `serializer` parameter tells DataStore the name of the serializer class defined in step 1.

### Kotlin

```kotlin
object SettingsSerializer : Serializer<Settings> {
  override val defaultValue: Settings = Settings.getDefaultInstance()

  override suspend fun readFrom(input: InputStream): Settings {
    try {
      return Settings.parseFrom(input)
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception)
    }
  }

  override suspend fun writeTo(
    t: Settings,
    output: OutputStream) = t.writeTo(output)
}

val Context.settingsDataStore: DataStore<Settings> by dataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)
```

### Java

```java
private static class SettingsSerializer implements Serializer<Settings> {
  @Override
  public Settings getDefaultValue() {
    Settings.getDefaultInstance();
  }

  @Override
  public Settings readFrom(@NotNull InputStream input) {
    try {
      return Settings.parseFrom(input);
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception);
    }
  }

  @Override
  public void writeTo(Settings t, @NotNull OutputStream output) {
    t.writeTo(output);
  }
}

RxDataStore<Byte> dataStore =
    new RxDataStoreBuilder<Byte>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();
```

### Read from a Proto DataStore

Use `DataStore.data` to expose a `Flow` of the appropriate property from your stored object.  

### Kotlin

```kotlin
val exampleCounterFlow: Flow<Int> = context.settingsDataStore.data
  .map { settings ->
    // The exampleCounter property is generated from the proto schema.
    settings.exampleCounter
  }
```

### Java

```java
Flowable<Integer> exampleCounterFlow =
  dataStore.data().map(settings -> settings.getExampleCounter());
```

### Write to a Proto DataStore

Proto DataStore provides an
[`updateData()`](https://developer.android.com/reference/kotlin/androidx/datastore/core/DataStore#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.  

### Kotlin

```kotlin
suspend fun incrementCounter() {
  context.settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}
```

### Java

```java
Single<Settings> updateResult =
  dataStore.updateDataAsync(currentSettings ->
    Single.just(
      currentSettings.toBuilder()
        .setExampleCounter(currentSettings.getExampleCounter() + 1)
        .build()));
```

## Use DataStore in synchronous code

| **Caution:** Avoid blocking threads on DataStore data reads whenever possible. Blocking the UI thread can cause [ANRs](https://developer.android.com/topic/performance/vitals/anr) or UI jank, and blocking other threads can result in [deadlock](https://en.wikipedia.org/wiki/Deadlock).

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()`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)
coroutine builder to help bridge the gap between synchronous and asynchronous
code. You can use `runBlocking()` to read data from DataStore synchronously.
RxJava offers blocking methods on `Flowable`. The following code blocks the calling
thread until DataStore returns data:  

### Kotlin

```kotlin
val exampleData = runBlocking { context.dataStore.data.first() }
```

### Java

```java
Settings settings = dataStore.data().blockingFirst();
```

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:  

### Kotlin

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        context.dataStore.data.first()
        // You should also handle IOExceptions here.
    }
}
```

### Java

```java
dataStore.data().first().subscribe();
```

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.

## Use DataStore in multi-process code

| **Note:** DataStore multi-process is currently available in the 1.1.0 release

You can configure DataStore to access the same data across different processes
with the same data consistency guarantees as from within a single process. In
particular, DataStore guarantees:

- Reads only return the data that has been persisted to disk.
- Read-after-write consistency.
- Writes are serialized.
- Reads are never blocked by writes.

Consider a sample application with a service and an activity:

1. The service is running in a separate process and periodically updates the
   DataStore

       <service
         android:name=".MyService"
         android:process=":my_process_id" />

   **Important:** To run the [service](https://developer.android.com/guide/topics/manifest/service-element) in a different process, use the android:process attribute. Note that the process id is prefixed with a colon (':'). This makes the service run in a new process, private to the application.  

       override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
             scope.launch {
                 while(isActive) {
                     dataStore.updateData {
                         Settings(lastUpdate = System.currentTimeMillis())
                     }
                     delay(1000)
                 }
             }
       }

2. While the app would collect those changes and update its UI

       val settings: Settings by dataStore.data.collectAsState()
       Text(
         text = "Last updated: $${settings.timestamp}",
       )

To be able to use DataStore across different processes, you need to construct
the DataStore object using the `MultiProcessDataStoreFactory`.  

    val dataStore: DataStore<Settings> = MultiProcessDataStoreFactory.create(
       serializer = SettingsSerializer(),
       produceFile = {
           File("${context.cacheDir.path}/myapp.preferences_pb")
       }
    )

`serializer` tells DataStore how to read and write your data type.
Make sure you include a default value for the serializer to be used if there is
no file created yet. Below is an example implementation using
[kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization):  

    @Serializable
    data class Settings(
       val lastUpdate: Long
    )

    @Singleton
    class SettingsSerializer @Inject constructor() : Serializer<Settings> {

       override val defaultValue = Settings(lastUpdate = 0)

       override suspend fun readFrom(input: InputStream): Settings =
           try {
               Json.decodeFromString(
                   Settings.serializer(), input.readBytes().decodeToString()
               )
           } catch (serialization: SerializationException) {
               throw CorruptionException("Unable to read Settings", serialization)
           }

       override suspend fun writeTo(t: Settings, output: OutputStream) {
           output.write(
               Json.encodeToString(Settings.serializer(), t)
                   .encodeToByteArray()
           )
       }
    }

You can use [Hilt](https://developer.android.com/training/dependency-injection/hilt-android) dependency
injection to make sure that your DataStore instance is unique per process:  

    @Provides
    @Singleton
    fun provideDataStore(@ApplicationContext context: Context): DataStore<Settings> =
       MultiProcessDataStoreFactory.create(...)

## Handle file corruption

There are rare occasions where DataStore's persistent on-disk file could get
corrupted. By default, DataStore doesn't automatically recover from corruption,
and attempts to read from it will cause the system to throw a
`CorruptionException`.

DataStore offers a corruption handler API that can help you recover gracefully
in such a scenario, and avoid throwing the exception. When configured, the
corruption handler replaces the corrupted file with a new one containing a
pre-defined default value.

To set up this handler, provide a `corruptionHandler` when creating the
DataStore instance in `by dataStore()` or in the `DataStoreFactory` factory
method:  

    val dataStore: DataStore<Settings> = DataStoreFactory.create(
       serializer = SettingsSerializer(),
       produceFile = {
           File("${context.cacheDir.path}/myapp.preferences_pb")
       },
       corruptionHandler = ReplaceFileCorruptionHandler { Settings(lastUpdate = 0) }
    )

## Provide feedback

Share your feedback and ideas with us through these resources:

[Issue tracker](https://issuetracker.google.com/issues/new?component=907884&template=1466542) :bug:
:   Report issues so we can fix bugs.

## Additional resources

To learn more about Jetpack DataStore, see the following additional resources:

### Samples

### Blogs

- [Prefer Storing Data with Jetpack DataStore](https://android-developers.googleblog.com/2020/09/prefer-storing-data-with-jetpack.html)

### Codelabs

- [Working with Preferences DataStore](https://codelabs.developers.google.com/codelabs/android-preferences-datastore)
- [Working with Proto DataStore](https://codelabs.developers.google.com/codelabs/android-proto-datastore)

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Load and display paged data](https://developer.android.com/topic/libraries/architecture/paging/v3-paged-data)
- [LiveData overview](https://developer.android.com/topic/libraries/architecture/livedata)
- [Layouts and binding expressions](https://developer.android.com/topic/libraries/data-binding/expressions)