Jetpack的DataStore数据存储组件的使用简介

Jetpack的DataStore数据存储组件的使用简介

介绍

Jetpack DataStore是一种数据存储解决方案,可让您使用Protocol Buffer(协议缓冲区)存储键值对或类型化对象。DataStore使用Kotlin协程Flow异步,一致的事务方式来存储数据。

注意: 如果需要支持大型或复杂的数据集,部分更新或参考完整性,请考虑使用 Room 而不是DataStore。DataStore非常适合小型,简单的数据集,并且不支持部分更新或参照完整性。

目的

Jetpack DataStore 是经过改进的新版数据存储解决方案,旨在取代 SharedPreferences。以异步、一致的事务方式存储数据,克服了 SharedPreferences 的大部分缺点。

DataStore实现方式

DataStore 基于 Kotlin 协程和流程构建而成,提供两种不同的实现,分别如下:

  • Preferences DataStore方式

使用键存储和访问数据。此实现不需要预定义的架构,并且不提供类型安全性。

  • Proto DataStore方式

将数据存储为自定义数据类型的实例。此实现要求您使用协议缓冲区定义架构,但它提供类型安全性。

SharedPreferences 与 DataStore 支持功能的对比

特征 SharedPreferences PreferencesDataStore ProtoDataStore
是否支持异步
是否支持同步
是否支持在UI线程调用
可以发出错误信号
避免运行时异常
类型安全
一致的事务方式存储数据

.

.

DataStore 的简单使用

DataStore的使用就是: Preferences DataStore 的使用 和 Proto DataStore 的使用

PreferencesDataStore 的使用

build.gradle 文件添加 DataStore 的依赖

dependencies {
    
    
  // Preferences DataStore
  implementation "androidx.datastore:datastore-preferences:1.0.0-alpha04"
}

.

PreferencesDataStore 的使用步骤如下:

1. 创建Preferences DataStore

//定义 DataStore 的名字
private val MY_DATA_STORE_NAME = "DataStorePreference"

//创建DataStore对象
val dataStore: DataStore<Preferences> = context.createDataStore(
  name = DATASTORE_PREFERENCE_NAME
)

注:
name属性必须要设置,不然无法使用 DataStore

2. 插入数据

private suspend fun saveData(value: String) {
    
    
		//创建key
        var preKey = preferencesKey<String>(PREFERENCE_KEY_NAME)
		//插入数据
        dataStore.edit {
    
     mutablePreferences ->
            mutablePreferences[preKey] = value
        }
    }

PreferencesDataStore 中是通过 DataStore.edit() 写入数据,edit 方法是个 suspend 函数,必须在协程中进行调用;

3. 根据key来读取数据

private suspend fun readDara(): String {
    
    
		//创建key
        var preKey = preferencesKey<String>(PREFERENCE_KEY_NAME)
		
		//根据key读取数据
        var value = dataStore.data.map {
    
     preferences ->
            preferences[preKey] ?: ""
        }
        return value.first()
}

4. 读取数据时处理异常时

private suspend fun readData(): String {
    
    
		//创建key
        var preKey = preferencesKey<String>(PREFERENCE_KEY_NAME)

		//根据key读取数据
        var value = dataStore.data
	    .catch {
    
     exception ->
			//进行异常处理
	        if (exception is IOException) {
    
    
	           //异常处理
	        } else {
    
    
				//抛出异常
	            throw exception
	        }
    	}.map {
    
     preferences ->
            preferences[preKey] ?: ""
        }
        return value.first()
}

补充:

关于Key的说明:

创建Key: var preKey = preferencesKey<String>(PREFERENCE_KEY_NAME)

key 的类型是 Preferences.Key,但是只支持 Int , String, Boolean , Float , Long 类型

从 SharedPreference 迁移数据

为了能够将其迁移到 DataStore,我们需要更新dataStore构建器以将传递SharedPreferencesMigration给迁移列表。

注:
DataStore将能够自动从SharedPreferences迁移到DataStore。必须先运行迁移,然后才能在DataStore中进行任何数据访问。这意味着在DataStore.data发出任何值和DataStore.edit()更新数据之前,您的迁移必须已经成功。

迁移代码:

//定义 DataStore 的名字
private val MY_DATA_STORE_NAME = "DataStorePreference"

val dataStore: DataStore<Preferences> = this.createDataStore(
    name = MY_DATA_STORE_NAME,
    migrations = listOf(SharedPreferencesMigration(this, USER_PREFERENCES_NAME))
)

.

.

ProtoDataStore 的使用

Proto DataStore实现使用 DataStoreprotocol buffers 将键入的对象持久保存到磁盘。

protocol buffers(协议缓冲区)是一种用于序列化结构化数据的机制。使用请查阅参考资料里的文章,这里由于篇幅就不展开介绍了,敬请原谅。

Proto DataStore的简单使用

build.gradle 文件添加 Proto DataStore 的依赖

  • 添加Protobuf插件
  • 添加Protobuf和ProtoDataStore依赖项
  • 配置Protobuf
//添加Protobuf插件
plugins {
    
    
	id "com.google.protobuf" version "0.8.12"
}

dependencies {
    
    

  // Proto DataStore 的依赖
  implementation  "androidx.datastore:datastore-core:1.0.0-alpha04"
  implementation  "com.google.protobuf:protobuf-javalite:3.10.0"
}

//配置Protobuf
protobuf {
    
    
    protoc {
    
    
        artifact = "com.google.protobuf:protoc:3.10.0"
    }

    generateProtoTasks {
    
    
        all().each {
    
     task ->
            task.builtins {
    
    
                java {
    
    
                    option 'lite'
                }
            }
        }
    }
}

.

ProtoDataStore 的使用步骤如下:

1. 创建 user_prefs.proto 来编写存放结构数据的模板

注:
记得在 app/src/main下创建名为 proto 的文件夹,并在 app/src/main/proto目录中创建一个新文件下创建名为 user_prefs.proto 的文件 ;在 user_prefs.proto文件里编写数据的模板。

syntax = "proto3";

option java_package = "com.codelab.android.datastore";
option java_multiple_files = true;

message UserPreferences {
    
    
  //用于显示/隐藏已完成任务的过滤器
  bool show_completed = 1;
}

注:
UserPreferences类是在编译时从所生成的message在原文件中定义。确保您重建项目。

2. 创建序列化器

为了告诉 DataStore 如何读写在原型文件中定义的数据类型,我们需要实现一个Serializer。如果磁盘上没有数据,则序列化程序还定义要返回的默认值。

创建序列化器类 UserPreferencesSerializer

object UserPreferencesSerializer : Serializer<UserPreferences> {
    
    
    override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()
    override fun readFrom(input: InputStream): UserPreferences {
    
    
        try {
    
    
            return UserPreferences.parseFrom(input)
        } catch (exception: InvalidProtocolBufferException) {
    
    
            throw CorruptionException("Cannot read proto.", exception)
        }
    }

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

注:
如果UserPreferences找不到对象或相关方法,请清理并重建项目以确保Protobuf生成了对象。

3. 创建数据存储区对象

作用: 通过这个对象就可以进行数据的 读取 和 添加 。

private val dataStore: DataStore<UserPreferences> =
    context.createDataStore(
        fileName = "user_prefs.pb",
        serializer = UserPreferencesSerializer)

4. 向 Proto DataStore中添加数据

suspend fun saveData(completed: Boolean) {
    
    
    dataStore.updateData {
    
     preferences ->
        preferences.toBuilder().setShowCompleted(completed).build()
    }
}

注:
Proto DataStore 中是通过 dataStore.updateData() 写入数据,updateData 方法是个 suspend 函数,必须在协程中进行调用;

updateData()函数会通过原子读写修改操作以事务方式更新数据。一旦数据保留在磁盘上,协程将完成。

5. 从Proto DataStore读取数据

val userPreferencesFlow: Flow<UserPreferences> = dataStore.data

注:

从ProtoDataStore中读取数据的返回值类型是Flow<UserPreferences>
UserPreferences是在user_prefs.proto文件定义的 message对象。

6. 当读取数据时异常的处理

由于 DataStore 从文件读取数据,因此在读取数据有可能会引起 IOException数据读取异常。我们可以使用 catch 来捕获异常,并对异常进行相关处理。如下所示:

private val TAG: String = "数据读取失败"

val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
    .catch {
    
     exception ->

        //数据读取失败
        if (exception is IOException) {
    
    
			//捕获异常(对异常进行处理)
            Log.e(TAG, "Error reading sort order preferences.", exception)
		
			emit(UserPreferences.getDefaultInstance())

        } else {
    
    
            throw exception
        }
    }

从 SharedPreference 迁移数据

为了帮助迁移,DataStore定义了SharedPreferencesMigration该类。让我们在中创建数据存储区对象的时候可以使用migrations参数来实现数据的迁移。

迁移步骤:

1. 编写迁移规则

  1. 检查 UserPreferences 中的 sortOrder 值。
  2. 如果是SortOrder.UNSPECIFIED这样,则意味着我们需要从SharedPreferences中检索值。如果 SortOrder 缺少,则可以使用SortOrder.NONE默认值。
  3. 获得排序顺序后,我们必须将 UserPreferences 对象转换为 builder,设置排序顺序,然后通过调用再次构建对象build()。此更改不会影响其他字段。
  4. 如果sortOrder的值UserPreferences不是,SortOrder.UNSPECIFIED我们只能返回我们获得的当前数据,migrate因为迁移必须已经成功运行
String SharedPreference_name = "填入需要迁移的SharedPreference的名字";

private val sharedPrefsMigration = SharedPreferencesMigration(context,SharedPreference_name) {
    
     
		//SharedPreferencesView:表示允许我们从SharedPreferences中检索数据
		sharedPrefs: SharedPreferencesView, 
		//UserPreferences:表示当前数据
		currentData: UserPreferences ->

        // 定义从SharedPreferences到UserPreferences的映射
        if (currentData.sortOrder == SortOrder.UNSPECIFIED) {
    
    
            currentData.toBuilder().setSortOrder(
                SortOrder.valueOf(
                    sharedPrefs.getString(
                        SORT_ORDER_KEY,SortOrder.NONE.name)!!
                )
            ).build()
        } else {
    
    
            currentData
        }
    }

2. 在创建数据存储对象的时候使用定义好的迁移逻辑

migrations包含我们的实例的新列表分配给参数SharedPreferencesMigration

private val dataStore: DataStore<UserPreferences> = context.createDataStore(
    fileName = "user_prefs.pb",
    serializer = UserPreferencesSerializer,
    migrations = listOf(sharedPrefsMigration)
)

通过以上两步就可以实现把数据从 SharedPreference 迁移到 ProtoDataStore 中,到处有关DataStore的使用就全部介绍完毕了,至于Protobuf语言 的语法请自行去学习,在这里就不做介绍了。

.

.

参考资料:

猜你喜欢

转载自blog.csdn.net/weixin_42324979/article/details/112650189
今日推荐