赞
踩
DataStore是google官方提供的,用于替换SharedPreferences来对简单数据进行存储的解决方案。对于SharedPreferences存在的缺陷
进行修补,并且可以和Kotlin协程与Flow结合。
DataStore提供对基本类型和对象类型进行分开存储,分别使用 PreferencesDataStore,ProtoDataStore.通过名字很容区分 PreferencesDataStore用来存储基本类型数据,String、int、long、double、float、boolean、set< String >。
Proto DataStore 使用协议缓冲区来定义架构。使用协议缓冲区可持久保留强类型数据。与 XML 和其他类似的数据格式相比,协议缓冲区速度更快、规格更小、使用更简单,并且更清楚明了。虽然使用 Proto DataStore 需要学习新的序列化机制,但 Proto DataStore 有着强大的类型优势,值得学习。
并且提供了方便的迁移接口,对于基础类型存储可以通过配置自动实现迁移。
PreferencesDataStore 配置依赖
dependencies {
//preferencesDatastore功能依赖
implementation "androidx.datastore:datastore-preferences:1.0.0"
//protoDatastore功能依赖
implementation "androidx.datastore:datastore-core:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
implementation "androidx.datastore:datastore:1.0.0"
}
//user_preferences 生成的文件名称 user_preferences.preferences_pb
private const val USER_PREFERENCES_NAME = "user_preferences"
private val Context.dataStore by preferencesDataStore(USER_PREFERENCES_NAME)
PreferencesDataStore 读取写入:需要放在协程中进行读写
private suspend fun spStoreSave() {
dataStore.edit { preferences ->
preferences[stringPreferencesKey("one1")] = "中华人民共和国"
preferences[intPreferencesKey("one2")] = 1
preferences[doublePreferencesKey("one3")] = 1.00
preferences[floatPreferencesKey("one4")] = 1f
preferences[longPreferencesKey("one5")] = 1L
preferences[booleanPreferencesKey("one6")] = true
preferences[stringSetPreferencesKey("one7")] = setOf("1", "2", "3", "4", "5", "6")
}
}
读取
private suspend fun getSpStore() {
val flow = dataStore.data.catch { exception ->
// dataStore.data throws an IOException when an error is encountered when reading data
if (exception is IOException) {
Log.e(TAG, "Error reading preferences.", exception)
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
println(preferences[stringPreferencesKey("one1")])
println(preferences[intPreferencesKey("one2")])
println(preferences[doublePreferencesKey("one3")])
println(preferences[floatPreferencesKey("one4")])
println(preferences[longPreferencesKey("one5")])
println(preferences[booleanPreferencesKey("one6")])
preferences[stringSetPreferencesKey("one7")]
}
flow.collect()
}
生成的文件内部结构 user_preferences.preferences_pb
SharedPreferences写入相同的数据后进行对比
private fun saveUserInfo() {
val userInfo = getSharedPreferences(SETTING, MODE_PRIVATE)
val editor = userInfo.edit() //获取Editor
editor.apply {
putString("one1", "中华人民共和国")
putInt("one2", 1)
putFloat("one3",1.00f)
putFloat("one4",1f)
putLong("one5",1L)
putBoolean("one6",true)
putStringSet("one7", setOf("1", "2", "3", "4", "5", "6"))
}
editor.commit() //提交修改
}
文件内部结构对比。左边为SharedPreferences存储,右边为PreferencesDataStore 存储。
相同写入相同内容,SharedPreferences比PreferencesDataStore存储大小多了3倍。
需要指定要迁入的 PreferencesDataStore 文件名称和要迁出的文件SharedPreferences文件名称,如下:
//SharedPreferences 旧存储结构SharedPreferences
private const val SETTING = "setting"
//要迁入的新存储结构 PreferencesDataStore
private const val USER_PREFERENCES_NAME = "user_preferences"
private val Context.dataStore by preferencesDataStore(
name = USER_PREFERENCES_NAME,
produceMigrations = { context ->
// Since we're migrating from SharedPreferences, add a migration based on the
// SharedPreferences name
listOf(SharedPreferencesMigration(context, SETTING))
}
)
重新编译运行项目后检查 设备路径:data/data/packageName/files/datastore/ xx.preferences_pb文件,如果文件中有SharedPreferences内容,表示自动迁移完成了。
Proto DataStore 是用来存取可序列化对象,SharedPreferences 不能直接对对象进行存取,开发中 一般做法会将对象转换成jsonString进行存储,需要取出时再由取出来的jsonStrinng转换成需要的Bean文件。如果使用Proto DataStore存取那么可以直接取出定义的Bean对象。
在项目app模块 build.gradle 文件中添加自动生成模板代码的插件和必要的依赖项
plugins {
id 'com.android.application'
id 'kotlin-android'
id "com.google.protobuf" version "0.8.17"
}
//protobuf闭包 与 android闭包同一级
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.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'
}
}
}
}
}
dependencies {
//protocol cache
implementation "androidx.datastore:datastore-core:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
implementation("androidx.datastore:datastore:1.0.0")
}
需要在**/app/src/main/proto**在建立一个后缀为.proto的 protoBuffer文件,比如这里创建一个名为
person.proto的文件。 之后需要手动进行编译,可以通过检查app/build/generrated/source/proto/debug/java/包名/下
syntax = "proto3";
// 默认生成路径 ${project.buildDir}/generated/source/proto
option java_package = "com.codelab.android.datastore";
option java_multiple_files = true;
message Person {
//学号
int32 id = 1;
//姓名
string name = 2;
//年龄
int32 age = 3;
//班级
string class = 4;
}
创建一个序列化器,都是一样的写法,照着模板写就行
//创建序列化器
object PersonPreferencesSerializer : Serializer<Person> {
override val defaultValue: Person = Person.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Person {
try {
return Person.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", e)
}
}
override suspend fun writeTo(t: Person, output: OutputStream) {
t.writeTo(output)
}
}
//存
lifecycleScope.launch {
persion.updateData { preferenceChanges ->
preferenceChanges.toBuilder()
.setId(10000)
.setAge(1)
.setName("小明")
.setClass_("一年级")
.build()
}
}
//取
lifecycleScope.launch {
persion.data.catch { exception ->
// dataStore.data throws an IOException when an error is encountered when reading data
if (exception is IOException) {
Log.e(TAG, "Error reading sort order preferences.", exception)
emit(Person.getDefaultInstance())
} else {
throw exception
}
}.map {
with(it) {
println("class =$class_ name =$name age =$age id =$id")
}
}.collect()
}
SharedPreferences 文件迁移到ProtoDataStore,需要手动取出来SharedPreferences内存储值,通过事务Build设置给Proto对象。需要注意的一点是每一个定义的proto 对象为一个单独的文件。如果SharedPreferences 内存储值需要转存入Proto对象中。需要在如下代码Listof(SharedPreferencesMigration(),SharedPreferencesMigration(), …)定义多个接收SharedPreferences值的对象。
private val Context.userPreferencesStore: DataStore<UserPreferences> by dataStore(fileName = 对象.pb文件名称,
serializer = UserPreferencesSerializer,
produceMigrations = { context ->
listOf(SharedPreferencesMigration(
context, Sp文件名称
) { sharedPreferences: SharedPreferencesView, currentData: UserPreferences ->
if (currentData.sortOrder == SortOrder.UNSPECIFIED) {
currentData.toBuilder().setSortOrder(
SortOrder.valueOf(
sharedPreferences.getString(
SORT_ORDER_KEY, SortOrder.NONE.name
)!!
)
).build()
} else {
currentData
}
})
})
对于简单数据存取使用SharedPreferences也有用MMKV替换SharedPreferences,Jetpack DataStore作为Google 官方推荐用来替换SharedPreferences的控件,在修复了SharedPreferences已知问题之外并提供了和当下流行的Kotlin 语言接口的api.在扩展方面有了保证。 用法上和SharedPreferences大同小异,具体使用SharedPreferences 还是DataStore,官方建议使用DataStore。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。