前言
DataStore提供了一种安全且持久的方式来存储少量数据。它不支持部分更新:如果任何字段被修改,整个对象将被序列化并持久到磁盘。它是线程安全的,非阻塞的。特别是,它解决了SharedPreferences这些设计缺陷:
-
同步API鼓励违反StrictMode
-
apply和commit没有发出错误信号的机制
-
apply将阻塞fsync上的UI线程
-
不持久-它可以返回尚未持久的状态
-
没有一致性或事务语义
-
在解析错误是引发运行时异常
-
公开对齐内部状态的可变引用
Preferences DataStore
用法
添加依赖
dependencies {
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.datastore:datastore-preferences-core:1.0.0"
}
创建Preference DataStore
val Context.datastore: DataStore
by preferencesDataStore(name = "test_datastore")
该方法只应在文件中调用一次(在顶层),并且DataStore的所有用法都应该使用同一个实例的引用。属性委托的接收者类型必须是Context类型。同时会生成一个命名为name..preferences_pb的文件。
存数据
通过edit方法写数据到文件:
lifecycleScope.launch {
context.datastore.edit {
it[stringPreferencesKey(key)] = value
}
}
edit是一个suspend方法,需要在协程里调用,其中key,value是一对键值。
取数据
通过Flow去获取数据:
lifecycleScope.launch {
context.datastore.data.collect {
value = it[floatPreferencesKey(key)]
}
}
value = datastore.data.first()[intPreferencesKey("test_key")]
通过以上俩种方式都可以获得相应key的value值。
Proto DataStore
用法
添加依赖
在app的build.gradle引入protobuf插件:
plugins {
...
id "com.google.protobuf" version "0.8.17"
}
android {
...
protobuf {
protoc {
//protoc版本参见:https://repo1.maven.org/maven2/com/google/protobuf/protoc/
artifact = "com.google.protobuf:protoc:3.18.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
//修改生成java类的位置 默认是 $buildDir/generated/source/proto
generatedFilesBaseDir = "$projectDir/src/main/generated"
}
}
dependencies {
implementation "androidx.datastore:datastore:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
}
定义架构
Proto DataStore要求在app/src/main/proto目录对的proto文件中保存定义的架构。此架构用于在Proto DataStore中保存的对象类型。
syntax = "proto3";
option java_package = "com.example.myapplication";
option java_multiple_files = true;
message MessageEvent {
int32 type = 1;
string message = 2;
}
创建Proto DataStore
1.定义一个实现Serializer
2.使用由datastore创建的属性委托来创建DataStore
object MessageEventSerializer : Serializer
{
override val defaultValue: MessageEvent = MessageEvent.getDefaultInstance()
override suspend fun readFrom(input: InputStream): MessageEvent {
try {
return MessageEvent.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", e)
}
}
override suspend fun writeTo(t: MessageEvent, output: OutputStream) {
t.writeTo(output)
}
}
val Context.messageDataStore: DataStore
by dataStore(
fileName = "message.pb",
serializer = MessageEventSerializer
)
存数据
ProtocolDatastore提供了一个updateData方法,用于以事务的方式更新存储的对象。它提供数据的当前状态,作为数据类型的一个实例,并在原子读-写-修改操作中以事务方式更新数据。
lifecycleScope.launch {
context.messageDataStore.updateData {
it.toBuilder().setMessage("message_proto").build()
}
}
读数据
使用datastore.data显示所存储对象中相应属性的Flow:
GlobalScope.launch {
context.messageDataStore.data.collect {
it.message
Log.i(TAG, "message="+it.message)
}
context.messageDataStore.data.first().message
}
总结
按官方数据来说,DataStore相比SharedPreference更强大,包括在UI线程调用安全,有错误信号通知,运行时异常安全,通过事务确保一致性,类型安全等。