Jetpack DataStore you always want to know about it?

Table of contents

1. Introduction to DataStore

Preferences DataStore 和 Proto DataStore

二、Preferences DataStore

2.1 Add dependencies

2.2 Use Preferences DataStore to store key-value pairs

2.2.1 Create DataStore

2.2.1 DataStore write data

2.2.3 DataStore read data

3. Proto DataStore

3.1 Define the architecture

3.2 Create Proto DataStore

4. Related Links


1. Introduction to DataStore

        DataStore is a component in Android Jetpack. It is a data storage solution. Like SharedPreferences, it is stored in the form of key-value.

        DataStore guarantees atomicity, consistency, isolation, and durability. In particular, it addresses a design flaw in the SharedPreferences API.

        Jetpack DataStore is a new and improved data storage solution designed to replace SharedPreferences, allowing applications to store data in an asynchronous, transactional manner.

Note: DataStore is more suitable for small data and simple operations, and cannot update data locally. If you need to support large or complex datasets, partial updates, or referential integrity, consider using Room instead of DataStore.

Preferences DataStore 和 Proto DataStore

  • Preferences DataStore: Similar to SharedPreferences, data is stored through key-value pairs. This implementation does not require a predefined schema and does not provide type safety.

  • Proto DataStore: Define the storage data type and structure through Protocol-Buffers to ensure type safety.

        This article focuses on the Preferences DataStore.

二、Preferences DataStore

        Similar to SharedPreferences, which store data via key-value pairs, this implementation does not require a predefined schema and does not provide type safety.

2.1 Add dependencies

        Add the following dependencies to the build.gradle corresponding to your project's app_module:

dependencies {
    //Typed DataStore (Typed API surface, such as Proto)
    implementation "androidx.datastore:datastore:1.0.0"
//    //可选 - RxJava2 support
//    implementation "androidx.datastore:datastore-rxjava2:1.0.0"
//    //可选 - RxJava3 support
    implementation "androidx.datastore:datastore-rxjava3:1.0.0"
    
    //Preferences DataStore (SharedPreferences like APIs)
    implementation "androidx.datastore:datastore-preferences:1.0.0"
//    // 可选 - RxJava2 support
//    implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0"
//    // 可选 - RxJava3 support
    implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0"
}

2.2 Use Preferences DataStore to store key-value pairs

        First look at the DataStore source code, DataStore is an interface.

package androidx.datastore.core

import kotlinx.coroutines.flow.Flow
import java.io.IOException

/**
 * DataStore数据存储提供了一种安全、持久的方式来存储少量数据,如 preferences 和应用程序状态。
 * 数据存储提供了ACID保证。它是线程安全的,并且不阻塞。特别是,它解决了SharedReferences API的这些设计缺陷:
 * 1. Synchronous API encourages StrictMode violations
 * 2. apply() and commit() have no mechanism of signalling errors
 * 3. apply() will block the UI thread on fsync()
 * 4. Not durable – it can returns state that is not yet persisted
 * 5. No consistency or transactional semantics
 * 6. Throws runtime exception on parsing errors
 * 7. Exposes mutable references to its internal state
 */
public interface DataStore<T> {
    public val data: Flow<T>

    public suspend fun updateData(transform: suspend (t: T) -> T): T
}

It can be seen from the above that DataStore is implemented based on coroutines and Flow .

  • data is a Flow object.

  • updateData() is used to update the object.

        And check other relevant source codes of DataStore, you will find that they are all developed based on Kotlin language. Google is quite persistent in pushing Kotlin.

2.2.1 Create DataStore

  • Datastore<Preferences>Instance created using preferencesDataStore(Kotlin) .

  • If you use RxJava, use RxPreferenceDataStoreBuilder.

        The required name parameter is the name of the Preferences DataStore.

        Here we are using RxJava:

        RxDataStore<Preferences> dataStore =
                new RxPreferenceDataStoreBuilder(this, /*name=*/ "datastore_sc").build();
        //创建使用的key
        Preferences.Key<String> nameKey = PreferencesKeys.stringKey("name");
        Preferences.Key<Integer> ageKey = PreferencesKeys.intKey("age");

        Default path: /data/data/com.scc.datastorage/files/datastore/datastore_sc.preferences_pb

        Two keys are created here, namely String type and Int type.

2.2.1 DataStore write data

    //关于 nameKey 和 ageKey 请看上面代码。
    putString(nameKey, "name-Scc");
    putInteger(ageKey, 25);    
    
    //2022/1/25 功能:存入String类型的数据
    private void putString(Preferences.Key<String> nameKey, String value) {
        dataStore.updateDataAsync(new Function<Preferences, Single<Preferences>>() {
            @Override
            public Single<Preferences> apply(Preferences preferences) throws Throwable {
                MutablePreferences mutablePreferences = preferences.toMutablePreferences();
                mutablePreferences.set(nameKey, value);
                return Single.just(mutablePreferences);
            }
        });
    }
    //2022/1/25 功能:存入Integer类型的数据
    private void putInteger(Preferences.Key<Integer> ageKey, Integer value) {
        dataStore.updateDataAsync(new Function<Preferences, Single<Preferences>>() {
            @Override
            public Single<Preferences> apply(Preferences preferences) throws Throwable {
                MutablePreferences mutablePreferences = preferences.toMutablePreferences();
                mutablePreferences.set(ageKey, value);
                return Single.just(mutablePreferences);
            }
        });
    }

        The two types written here are more imitative to understand and you can write your own tool classes, because the use of DataStore is relatively seldom practiced and no tool classes are provided, so as not to mislead everyone.

2.2.3 DataStore read data

    getString(nameKey);
    getInteger(ageKey);
    
    //2022/1/25 功能:获取String类型数据
    private void getString(Preferences.Key<String> nameKey) {
        Log.e("DataStore", nameKey.toString());
        Flowable<String> example = dataStore.data().map(new Function<Preferences, String>() {
            @Override
            public String apply(Preferences preferences) {
                Log.e("DataStore.apply", preferences.get(nameKey));
                return preferences.get(nameKey);
            }
        });
        Log.e("DataStore.Flowable", example.first("default").blockingGet());
    }
    //2022/1/25 功能:获取Integer类型数据
    private void getInteger(Preferences.Key<Integer> ageKey) {
        Log.e("DataStoreKey", ageKey.toString());
        Flowable<Integer> example = dataStore.data().map(new Function<Preferences, Integer>() {
            @Override
            public Integer apply(Preferences preferences) {
                Log.e("DataStore.apply", preferences.get(ageKey).intValue() + "");
                return preferences.get(ageKey);
            }
        });
        Log.e("DataStore.Flowable", example.first(12).blockingGet().toString());
    }

3. Proto DataStore

        Define storage data types and structures through Protocol-Buffers to ensure type safety.

        The Proto DataStore implementation uses DataStore and Protocol-Buffers to persist typed objects to disk.

What are Protocol-Buffers?

        Protocol-Buffers is Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data -- like XML, but smaller, faster, and simpler . You only need to define how your data is structured once, and then use specially generated source code to easily write and read structured data across various data streams and in various languages.

3.1 Define the architecture

        The Proto DataStore requires predefined schemas in proto files in the app/src/main/proto/ directory. This schema defines the types of objects that you persist in the Proto DataStore.

syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.scc.datastorage.proto";
option java_outer_classname = "User";

message User{
  string name = 1;
  int32 age = 2;
}

The .proto file starts with a package declaration, which helps prevent naming conflicts between different projects.

  • java_multiple_files : Can generate a separate .java file for each generated class.

  • java_package : Specifies what Java package name the generated classes should use. If you don't specify it explicitly, it will only match the package name given by the package declaration.

  • java_outer_classname : defines the class name. If this option is not set, it will be generated by converting the filename to upper camel case. For example, "my_proto.proto" will use "MyProto" as the wrapper class name by default.

Define message:

  • Many standard simple data types can be used as field types, including bool, int32, float, double, and string.

  • You can also add further structure to your messages by using other message types as field types.

The "=1", "=2" marks         on each element identify the unique "tag" used in binary encoding for that field .

        For more information, you can find the protobuf language guide on the official website .

Note: The class that stores the object is generated from the message defined in the proto file at compile time. Make sure you rebuild your project.

3.2 Create Proto DataStore

  • 1. Define a Serializer<T>class that implements , where T is the type defined in the proto file.

  • 2. Use the attribute delegation created by dataStore to create DataStore<T>an instance of , where T is the type defined in the proto file.

        It is written here that the classes defined in the proto file cannot be generated, and I have tried many methods, but I don’t know what the situation is. For the next steps, please refer to the official website. Other methods are similar to Preferences DataStore. It doesn't take up space.

4. Related Links

Official document DataStore

        Personally, I feel that DataStore is not as easy to use as SP and MMKV. It is recommended to use MMKV. After all, it has been online for several years, and it is launched by a big factory. It is relatively stable. Some individuals have realized the function of SP by themselves, but in case there is a bug or suddenly they do not continue to maintain Is it embarrassing.

 

 

Guess you like

Origin blog.csdn.net/g984160547/article/details/122730602
Recommended