Jetpack DataStore is a data storage solution that allows you to store key-value pairs or typed objects using protocol buffers. DataStore uses Kotlin coroutines and Flows to store data in an asynchronous, consistent transactional manner.

Note: If you need to support large or complex data sets, partial updates, or referential integrity, consider using Room instead of DataStore. DataStore is well suited for simple, small data sets that do not support partial updates or referential integrity.

DataStore provides two different implementations: Preferences DataStore and Proto DataStore.

  • The Preferences DataStore uses keys to store and access data. This implementation does not require a predefined schema and does not ensure type safety.

  • Proto DataStore stores data as instances of custom data types. This implementation requires you to define schemas using protocol buffers, but ensures type safety.

The advantage of the DataStore

  • Based on theCoroutine Flowimplementation
  • Ensure data access consistency
  • Exception handling mechanism
  • Asynchronous access to avoid synchronous blocking
  • Based on theProtobufTo realize the storage of non-basic data

Contrast SharePreference

  • SharedPreferences has a synchronization API that looks like it can be safely called from the UI thread, but that API actually performs disk I/O operations. In addition, the apply() method blocks the UI thread at fsync(). Waiting for fsync() is triggered whenever a Service or Activity starts or stops anywhere in your application. Fsync () calls scheduled by apply() block the UI thread, which is also a common source of ANR.
  • SharedPreferences throws a runtime exception when parsing errors occur.

Official comparison:

Use the Preferences DataStore to store key-value pairs

Rely on

// Typed DataStore (Typed API surface, such as Proto)
dependencies {
  implementation "Androidx. Datastore: datastore: 1.0.0 - alpha08"

  // optional - RxJava2 support
  implementation "Androidx. Datastore: datastore - rxjava2:1.0.0 - alpha08"
}
Copy the code

The Preferences DataStore implementation uses the DataStore and Preferences classes to keep simple key-value pairs on disk.

Create the Preferences DataStore

Use the property delegate created by preferencesDataStore to create the Datastore

instance. Invoking the instance once at the top of your Kotlin file makes it accessible through this property throughout the rest of the application. This makes it easier to keep datastores as singletons. In addition, if you are using a RxJava, please use RxPreferenceDataStoreBuilder. The required name parameter is the name of the Preferences DataStore.

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

/ / practice
// Key in the file
Preferences.Key<String> USER_NAME = PreferencesKeys.stringKey("USER_NAME");
Preferences.Key<String> USER_PASSWD = PreferencesKeys.stringKey("USER_PASSWD");
// Create the Preferences DataStore
// You can view the USER_NAME. Preferences_pd file in the files/datastore directory
RxDataStore<Preferences> dataStore = new RxPreferenceDataStoreBuilder(mContext, "USER_NAME").build();
Copy the code

Read content from the Preferences DataStore

Because the Preferences DataStore does not use a predefined schema, you must define a key for each value that needs to be stored in the DataStore

instance using the corresponding key-type function. For example, to define a key for an int value, use intPreferencesKey(). Then, use the datastore.data attribute to supply the appropriate stored value through the Flow.

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

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

// Practice -- read the username and password from a file
// Read the Preferences DataStore data using the get method
Flowable<String> userNameFlow =
    dataStore.data().map(prefs -> prefs.get(USER_NAME));
Flowable<String> userPasswdFlow =
    dataStore.data().map(prefs -> prefs.get(USER_PASSWD));
try {
    // Fill in the text box
    accountEditText.setText(userNameFlow.blockingFirst());
    passwdEditText.setText(userPasswdFlow.blockingFirst());
}catch (RuntimeException e) {
    e.printStackTrace();
}
Copy the code

Write the content to the Preferences DataStore

The Preferences DataStore provides an edit() function that updates data in the DataStore transactionally. The function’s transform parameter takes a block of code in which you can update values as needed. All code in a transformation block is treated as a single transaction.

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);
});

/ / practice
// Write data using the username as the KEY
// VALUE is a String
Single<Preferences> updateResult =  dataStore.updateDataAsync(prefsIn -> {
    MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();

    // String userName = prefsIn.get(USER_NAME); // This method can get the value of the key

    / / will be key to write the key: USER_NAME | value: the account
    mutablePreferences.set(USER_NAME, account);
    / / will be key to write the key: USER_PASSWD | value: passwd
    mutablePreferences.set(USER_PASSWD, passwd);

    return Single.just(mutablePreferences);
});
Copy the code