文章目录
- Jetpack DataStore
- 概述
- DataStore 对比 SP
- 添加依赖库
- Preferences DataStore
- 路径
- 创建 Preferences DataStore
- 获取数据
- 保存数据
- 修改数据
- 删除数据
- 清除全部数据
- Proto DataStore
- 配置
- AndroidStudio安装插件
- 配置proto文件
- 创建序列化器
- 创建 Proto DataStore
- 获取数据
- 保存数据
- 修改数据
- 删除Map数据
- 清除数据
- 代码下载
Jetpack DataStore
概述
Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。
DataStore 提供两种不同的实现:Preferences DataStore 和 Proto DataStore。
- Preferences DataStore 使用键存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。
- Proto DataStore 将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。
DataStore 官方文档
Proto3 语法入门
Proto3 官方语法指南
DataStore 对比 SP
SharedPreference(简称SP) 是一个轻量级的数据存储方式,使用方便,以键值对的形式存储在本地。
SP的缺点:
- SP不能保证类型安全。如果存的数据和取的数据的类型不一致时会报异常。
- SP加载的数据会一直停留在内存中。
- 不支持多进程。
- 读写性能差,可能阻塞UI线程,可能引起ANR。
DataStore优点:
- 读写性能高。基于协程和Flow保证了UI线程的安全性。
- 从一定程度上保证类型安全。
添加依赖库
project/build.gradle
buildscript {
dependencies {
// Proto DataStore
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19'
}
}
module/build.gradle
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.protobuf'
}
dependencies {
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.datastore:datastore-core:1.0.0"
// Proto DataStore
implementation 'androidx.datastore:datastore-core:1.0.0'
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.0"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.0"
}
// 为该项目中的 Protobufs 生成 java Protobuf-lite 代码。
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
Preferences DataStore
路径
DataStore 生成的缓存文件存放在 /data/data/<包名>/files/datastore
目录下:
创建 Preferences DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_info")
获取数据
lifecycleScope.launch {
dataStore.edit { preferences ->
// 先通过 stringPreferencesKey() 方法获取指定Key
val nameKey = stringPreferencesKey("name")
val ageKey = intPreferencesKey("age")
val sexKey = booleanPreferencesKey("sex")
// 通过Key获取值
val name = preferences[nameKey]
val age = preferences[ageKey]
val sex = preferences[sexKey]
logE("name:$name age:$age sex:$sex")
}
}
保存数据
dataStore.edit { preferences ->
preferences[stringPreferencesKey("name")] = "小明"
preferences[intPreferencesKey("age")] = 18
preferences[booleanPreferencesKey("sex")] = true
}
修改数据
dataStore.edit { preferences ->
preferences[stringPreferencesKey("name")] = "小黑"
preferences[intPreferencesKey("age")] = 28
preferences[booleanPreferencesKey("sex")] = false
}
删除数据
dataStore.edit { preferences ->
val removeValue = preferences.remove(stringPreferencesKey("name"))
logE("remove:$removeValue")
}
清除全部数据
dataStore.edit { preferences ->
preferences.clear()
}
Proto DataStore
配置
AndroidStudio安装插件
配置proto文件
先新建 proto 目录:
再创建 person.proto 文件,并写入:
syntax = "proto3";
option java_package = "com.example.datastoredemo"; //设置生成的类所在的包
option java_multiple_files = true; //可能会有多个文件。
message PersonPreferences {
string name = 1; //String类型
int32 age = 2; //int类型
bool sex = 3; //boolean类型
repeated string address = 4; //String[]数组
map<string, string> fruits = 5; //Map类型
}
创建序列化器
object PersonSerializer : Serializer<PersonPreferences> {
override val defaultValue: PersonPreferences
get() = PersonPreferences.getDefaultInstance()
override suspend fun writeTo(t: PersonPreferences, output: OutputStream) {
t.writeTo(output)
}
override suspend fun readFrom(input: InputStream): PersonPreferences {
try {
return PersonPreferences.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
}
创建 Proto DataStore
val Context.personDataStore: DataStore<PersonPreferences> by dataStore(
fileName = "person.pb", serializer = PersonSerializer
)
获取数据
lifecycleScope.launch {
personDataStore.data.first().let { preferences ->
val name = preferences.name
val age = preferences.age
val sex = preferences.sex
val address = preferences.addressList
val fruits = preferences.fruitsMap
logE("name:$name age:$age sex:$sex address:$address fruits:$fruits")
}
}
保存数据
preferences.toBuilder()
.setName("小白")
.setAge(28)
.setSex(true)
.addAddress("广东省")
.addAddress("广州市")
.addAddress("黄埔区")
.putFruits("apple", "苹果")
.putFruits("banner", "香蕉")
.putFruits("cherry", "樱桃")
.build()
preferences.toBuilder()
.setName("小白")
.setAge(28)
.setSex(true)
.addAllAddress(listOf("广东省", "广州市", "黄埔区"))
.putAllFruits(mapOf("apple" to "苹果", "banner" to "香蕉", "cherry" to "樱桃"))
.build()
修改数据
personDataStore.updateData { preferences -
preferences.toBuilder()
.setName("小黑")
.setAge(38)
.setSex(false)
.setAddress(0, "湖南省")
.setAddress(1, "长沙市")
.setAddress(2, "芙蓉区")
.putFruits("apple", "苹果1号")
.build()
}
删除Map数据
personDataStore.updateData { preferences ->
preferences.toBuilder()
.removeFruits("apple") // 删除map数据
.build()
}
清除数据
personDataStore.updateData { preferences ->
// 清除所有数据
preferences.toBuilder()
.clear()
.build()
// 依次清除数据
preferences.toBuilder()
.clearName()
.clearAge()
.clearSex()
.clearAddress()
.clearFruits()
.build()
}
personDataStore.updateData { preferences ->
// 清除所有数据
preferences.toBuilder()
.clear()
.build()
// 依次清除数据
preferences.toBuilder()
.clearName()
.clearAge()
.clearSex()
.clearAddress()
.clearFruits()
.build()
}