Jetpack DataStore 你总要了解一下吧?

举报
帅次 发表于 2022/01/28 11:39:43 2022/01/28
【摘要】 DataStore是 Android Jetpack 中的一个组件,它是一个数据存储的解决方案,跟SharedPreferences一样,采用key-value形式存储。

一、DataStore 介绍

DataStore 是 Android Jetpack 中的一个组件,它是一个数据存储的解决方案,跟 SharedPreferences 一样,采用key-value形式存储。DataStore 保证原子性,一致性,隔离性,持久性。尤其是,它解决了 SharedPreferences API 的设计缺陷。Jetpack DataStore 是经过改进的新版数据存储解决方案,旨在取代 SharedPreferences,让应用能够以异步、事务方式存储数据。

注意:DataStore 比较适合小数据和简单操作,并且无法局部的更新数据。如果你需要支持大型或复杂的数据集、部分更新或引用完整性,请考虑使用 Room 而不是 DataStore。

Preferences DataStore 和 Proto DataStore

  • Preferences DataStore:与SharedPreferences类似,通过键值对存储数据,此实现不需要预定义模式,也不提供类型安全。
  • Proto DataStore:通过Protocol-Buffers定义存储数据类型以及结构,保证类型安全。

本文重点了解Preferences DataStore。

二、Preferences DataStore

与SharedPreferences类似,通过键值对存储数据,此实现不需要预定义模式,也不提供类型安全。

2.1 添加依赖

在你项目的app_module对应的build.gradle中添加如下依赖:

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 使用 Preferences DataStore 存储键值对

首先看看 DataStore 源码,DataStore 是一个接口。

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
}

以上可以看出 DataStore 是基于 协程 和 Flow 实现的。

  • data 是一个 Flow 对象。
  • updateData() 用于更新对象。

并且查看 DataStore 的其他相关源码你会发现他们都是基于Kotlin语言开发。Google 对于推 Kotlin 那是相当执着。

2.2.1 创建 DataStore

  • 使用 preferencesDataStore(Kotlin) 创建Datastore<Preferences> 的实例。
  • 如果你使用 RxJava,要使用 RxPreferenceDataStoreBuilder。

必需的 name 参数是 Preferences DataStore 的名称。

这里我们使用的是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");

默认路径:/data/data/com.scc.datastorage/files/datastore/datastore_sc.preferences_pb

这里创建了两个key,分别是 String 类型和 Int 类型。

2.2.1 DataStore 写入数据

    //关于 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);
            }
        });
    }

这里写入的两种类型,这样更加仿版理解和你写成自己的工具类,因为使用 DataStore 比较少实践少就不提供工具类了,以免误导大家。

2.2.3 DataStore 读取数据

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

三、Proto DataStore

通过Protocol-Buffers定义存储数据类型以及结构,保证类型安全。

Proto DataStore 实现使用 DataStore 和 Protocol-Buffers 将类型化对象持久保存到磁盘。

什么是 Protocol-Buffers?

Protocol-Buffers是谷歌的语言中立、平台中立、可扩展的机制,用于序列化结构化数据——比如XML,但更小、更快、更简单。您只需定义一次数据的结构化方式,然后就可以使用特殊生成的源代码,轻松地在各种数据流之间以及使用各种语言编写和读取结构化数据。

3.1 定义架构

Proto DataStore 需要app/src/main/proto/目录中的 proto 文件中的预定义模式。此架构定义了您在 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;
}

.proto 文件以包声明开头,这有助于防止不同项目之间的命名冲突。

  • java_multiple_files:可以为每个生成的类生成一个单独的 .java 文件。
  • java_package:指定生成的类应该使用什么 Java 包名称。如果您没有明确指定,它只会匹配包声明给出的包名。
  • java_outer_classname:定义了类名。如果没有设置这个 options ,它将通过将文件名转换为大写驼峰式来生成。例如,默认情况下,“my_proto.proto”将使用“MyProto”作为包装类名称。

定义 message:

  • 可以使用许多标准的简单数据类型可用作字段类型,包括 bool、int32、float、double 和 string。

  • 还可以通过使用其他 message 类型作为字段类型来为你的消息添加进一步的结构。

每个元素上的 "= 1"、"= 2" 标记标识该字段在二进制编码中使用的唯一 "tag"

更多关于 的内容可以去官网找找 protobuf 语言指南

注意:存储对象的类是在编译时从 proto 文件中定义的message生成的。确保你 rebuild 你的项目。

3.2 创建 Proto DataStore

  • 1.定义一个实现 Serializer<T> 的类,其中 T 是 proto 文件中定义的类型。
  • 2.使用 dataStore 创建的属性委托来创建 DataStore<T> 的实例,其中 T 是 proto 文件中定义的类型。

写到这里 proto 文件中定义的类无法生成,也尝试了很多办法,不知道什么情况。后续步骤可参照官网,其他方法和Preferences DataStore类似。就不占用篇幅了。

相关链接

官方文档 DataStore;

个人感觉 DataStore 还是没有 SP 和 MMKV 好用,推荐使用 MMKV 毕竟上线好几年了,而且是大厂推出,相对稳定一些,有些个人自己实现了 SP 的功能,但是万一有 Bug 或者突然不继续维护了是不是就尴尬了。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。