新的 Jetpack Datastore 是否仅适用于 Kotlin?

Is new Jetpack Datastore only for Kotlin?

我已经阅读了 Jetpack 的新库(现在处于 alpha 阶段)- Jetpack Datastore

从文档中可以清楚地看出它是一种 Shared Preferences' killer

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

如果我没有遗漏任何你不能在 Java 中使用这个库的东西。我对吗?我个人使用 Kotlin,但对我来说,这是 AndroidX 库的一个特殊先例。

我很确定他们没有计划 java。

Built on Kotlin coroutines and Flow, DataStore provides two different implementations: Proto DataStore, that lets you store typed objects (backed by protocol buffers) and Preferences DataStore, that stores key-value pairs. Data is stored asynchronously, consistently, and transactionally, overcoming most of the drawbacks of SharedPreferences.

就我而言,java 无法使用 Kotlin coroutines/flow。 您可以阅读更多 here,一篇来自 Florina 的精彩文章。

2021 年 1 月 13 日

Version 1.0.0-alpha06 已发布。添加了对 RxJava 2/3 的支持,因此现在可以在 Java 中使用 Datastore。

Added RxJava wrappers for DataStore. The datastore-rxjava2/3 artifacts contain the wrappers for the core DataStore APIs (RxDataStore, RxDataStoreBuilder, and RxDataMigration). The datastore-preferences-rxjava2/3 artifacts contain a builder to construct a Preferences DataStore.

为此你应该添加依赖项:

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

// optional - RxJava3 support
implementation "androidx.datastore:datastore-rxjava3:1.0.0-alpha06"

此外,现在 Datastore's official documentation 包含 Java 的代码示例等价物。

您现在可以使用 DataStore only in RxJava. In plain java you can use only SharedPreferences。让我们比较一下 RxJava DataStore Preferences 和 SharedPreferences

1) 访问

数据存储:

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

共享首选项:

SharedPreferences sharedPref = context.getSharedPreferences(
        getString(R.string.preference_file_key), Context.MODE_PRIVATE);

2) 阅读:

数据存储: 为您的值定义一个键(例如 int 值),然后访问数据存储区的数据:PreferencesKeys.int("example_counter")

datastore.data().map()

data() - 访问DataStore的数据。这个属性returns一个Flow

map() - returns a Flow 其中包含将给定函数应用于原始 Flow

的每个值的结果

SharedPreferences

sharedPref.getInt("highScoreKey", 0);

要从共享首选项文件中检索值,请使用默认值(此处为 o)提供您想要的值的键

3) 写入

数据存储:

dataStore.updateDataAsync()

以事务方式更新 DataStore 中的数据

共享首选项:

使用SharedPreferences.Editor 传递您要使用putInt() 和putString() 等方法编写的键和值。然后调用 apply() 或 commit() 来保存更改。 Example

结论: developer.android.com 建议考虑迁移到 DataStore 而不是 SharedPreferences。但是 Java 不支持 DataStore,最好使用 SharedPreferences。如果您的应用程序使用 Kotlin 或 RxJava - 最好使用 DataStore

我同意@Victor Pozdnyakov 的回答。所以,我不会在这里重申这一点,但我真的很难让它在 java 中工作,只是为了保存一个值;我没有找到任何 tuts,所以我不得不深入研究 RxJava 文档。

这是一个存储 int 值的示例,return 它没有连续的数据观察(就像你对 SharedPreferences 所做的那样)

依赖关系:

implementation "androidx.datastore:datastore-preferences:1.0.0"

// RxJava3 support
implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0"

implementation "io.reactivex.rxjava3:rxandroid:3.0.0"

示例提供了描述注释:

public class MainActivity extends AppCompatActivity {

    // Data store object
    RxDataStore<Preferences> dataStore =
            new RxPreferenceDataStoreBuilder(this, "settings").build();

    // Key for saving integer value
    Preferences.Key<Integer> SOME_KEY = PreferencesKeys.intKey("SOME_KEY");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Storing an int value to DataStore with the key of SOME_KEY
        saveInt(SOME_KEY, 1050);

        // Read the data immediately as it's already observed
        observeInt(SOME_KEY, value -> runOnUiThread(() ->
                Toast.makeText(this, "Observed Value: " + value, Toast.LENGTH_SHORT).show()));


        // Wait some time before reading the data as it takes time until it's stored; 
        // so don't call it here, but on some other event
        readInt(SOME_KEY, value -> runOnUiThread(() ->
                Toast.makeText(this, "Value: " + value, Toast.LENGTH_SHORT).show()));

    }

    /**
     * Saving an int value to DataStore with some key
     *
     * @param key:   The key associated to the value need to be stored
     * @param value: The value to be stored
     */
    private void saveInt(Preferences.Key<Integer> key, int value) {
        dataStore.updateDataAsync(prefsIn -> {
            MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
            mutablePreferences.set(key, value);
            return Single.just(mutablePreferences);
        }).subscribe();

    }

    /**
     * Returning an int value from the DataStore which is associated to some key,
     * once the result is returned, the subscription is disposed.
     *
     * @param key:      The key associated to the value need to be stored
     * @param listener: The value is returned in a worker thread, and returned to the
     *                  caller using a listener pattern
     */
    public void readInt(Preferences.Key<Integer> key, IntListener listener) {
        Flowable<Integer> flowable =
                dataStore.data().map(prefs -> prefs.get(key));

        flowable.firstOrError().subscribeWith(new DisposableSingleObserver<Integer>() {

            @Override
            public void onSuccess(@NotNull Integer value) {
                listener.intValue(value);
            }

            @Override
            public void onError(@NotNull Throwable error) {
                error.printStackTrace();
            }
        }).dispose();
    }


    /**
     * Subscribing an observer to an int value in the DataStore which is associated to some key,
     * The subscription submits any change to the value
     *
     * @param key:      The key associated to the value need to be stored
     * @param listener: The value is returned in a worker thread, and returned to the
     *                  caller using a listener pattern
     */
    public void observeInt(Preferences.Key<Integer> key, IntListener listener) {
        Flowable<Integer> flowable =
                dataStore.data().map(prefs -> prefs.get(key));

        flowable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()) // AndroidSchedulers requires ` implementation "io.reactivex.rxjava3:rxandroid:3.0.0" `
                .subscribe(new FlowableSubscriber<Integer>() {

                    @Override
                    public void onSubscribe(@NonNull Subscription s) {
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(Integer value) {
                        listener.intValue(value);
                    }

                    @Override
                    public void onError(Throwable t) {
                        t.printStackTrace();
                    }

                    @Override
                    public void onComplete() {
                    }
                });

    }


    interface IntListener {
        void intValue(int value);
    }

}