赞
踩
Jetpack DataStore
是一种数据存储解决方案,可让您使用Protocol Buffer
(协议缓冲区)存储键值对或类型化对象。DataStore使用Kotlin协程
和Flow
异步,一致的事务方式来存储数据。
注意: 如果需要支持大型或复杂的数据集,部分更新或参考完整性,请考虑使用
Room
而不是DataStore。DataStore非常适合小型,简单的数据集,并且不支持部分更新或参照完整性。
Jetpack DataStore
是经过改进的新版数据存储解决方案,旨在取代 SharedPreferences
。以异步、一致的事务方式存储数据,克服了 SharedPreferences
的大部分缺点。
DataStore 基于 Kotlin 协程和流程构建而成,提供两种不同的实现,分别如下:
使用键存储和访问数据。此实现不需要预定义的架构,并且不提供类型安全性。
将数据存储为自定义数据类型的实例。此实现要求您使用协议缓冲区定义架构,但它提供类型安全性。
特征 | SharedPreferences | PreferencesDataStore | ProtoDataStore |
---|---|---|---|
是否支持异步 | 是 | 是 | 是 |
是否支持同步 | 是 | 否 | 否 |
是否支持在UI线程调用 | 否 | 是 | 是 |
可以发出错误信号 | 否 | 是 | 是 |
避免运行时异常 | 否 | 是 | 是 |
类型安全 | 否 | 否 | 是 |
一致的事务方式存储数据 | 否 | 是 | 是 |
.
.
DataStore的使用就是: Preferences DataStore 的使用 和 Proto DataStore 的使用
在 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
类型
为了能够将其迁移到 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))
)
.
.
Proto DataStore实现使用 DataStore
和 protocol buffers
将键入的对象持久保存到磁盘。
protocol buffers
(协议缓冲区)是一种用于序列化结构化数据的机制。使用请查阅参考资料里的文章,这里由于篇幅就不展开介绍了,敬请原谅。
在 build.gradle
文件添加 Proto DataStore 的依赖
//添加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 } }
为了帮助迁移,
DataStore
定义了SharedPreferencesMigration
该类。让我们在中创建数据存储区对象的时候可以使用migrations参数来实现数据的迁移。
迁移步骤:
1. 编写迁移规则
- 检查
UserPreferences
中的sortOrder
值。- 如果是
SortOrder.UNSPECIFIED
这样,则意味着我们需要从SharedPreferences
中检索值。如果SortOrder
缺少,则可以使用SortOrder.NONE
默认值。- 获得排序顺序后,我们必须将
UserPreferences
对象转换为builder
,设置排序顺序,然后通过调用再次构建对象build()
。此更改不会影响其他字段。- 如果
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语言 的语法请自行去学习,在这里就不做介绍了。
.
.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。