目前好像就在1.0.0版本了
Preferences DataStore 和 Proto DataStore
DataStore 提供两种不同的实现:Preferences DataStore 和 Proto DataStore。
- Preferences DataStore 使用键存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。
- Proto DataStore 将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。
1. Preferences DataStore
implementation("androidx.datastore:datastore-preferences:1.0.0")
开始使用,先创建扩展对象,
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
这个存储虽说也是一对一,不是这里的键不是以前的string,它是一个类Preferences.Key,但是了,不用担心,系统都给安排好了,如下,你要存储啥类型的数据,就用下边的方法即可,直接返回给你要的Key
@JvmName("intKey")
public fun intPreferencesKey(name: String): Preferences.Key<Int> = Preferences.Key(name)
@JvmName("doubleKey")
public fun doublePreferencesKey(name: String): Preferences.Key<Double> = Preferences.Key(name)
@JvmName("stringKey")
public fun stringPreferencesKey(name: String): Preferences.Key<String> = Preferences.Key(name)
@JvmName("booleanKey")
public fun booleanPreferencesKey(name: String): Preferences.Key<Boolean> = Preferences.Key(name)
@JvmName("floatKey")
public fun floatPreferencesKey(name: String): Preferences.Key<Float> = Preferences.Key(name)
@JvmName("longKey")
public fun longPreferencesKey(name: String): Preferences.Key<Long> = Preferences.Key(name)
@JvmName("stringSetKey")
public fun stringSetPreferencesKey(name: String): Preferences.Key<Set<String>> =
Preferences.Key(name)
读数据
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = context.dataStore.data
.map { preferences ->
// No type safety.
preferences[EXAMPLE_COUNTER] ?: 0
}
写数据
suspend fun incrementCounter() {
context.dataStore.edit { settings ->
val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
settings[EXAMPLE_COUNTER] = currentCounterValue + 1
}
}
下边演示下使用compose动态修改主题颜色的代码,如下是自定义theme的代码
private val LightThemeColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800,
onBackground = Color.Black,
)
private val LightThemeColors2 = lightColors(
primary = Blue700,
primaryVariant = Blue900,
onPrimary = Color.White,
secondary = Blue700,
secondaryVariant = Blue900,
onSecondary = Color.White,
error = Blue800,
onBackground = Color.Black,
)
private val DarkThemeColors = darkColors(primary = Red300,
primaryVariant = Red700,
onPrimary = Color.Black,
secondary = Red300,
onSecondary = Color.Black,
error = Red200,
onBackground = Color.White)
val Context.dataStore by preferencesDataStore("mytheme")
//默认的主题themeid为0,另外一套为1
val themeKey: Preferences.Key<Int>
get() = intPreferencesKey("mythemekey")
@Composable
fun JetnewsTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val context = LocalContext.current
val theme = remember {
context.dataStore.data.map { preferences ->
preferences[themeKey] ?: 0
}
}
val mytheme = theme.collectAsState(initial = 0).value
val lightTheme = if (mytheme == 0) LightThemeColors else LightThemeColors2
MaterialTheme(colors = if (darkTheme) DarkThemeColors else lightTheme,
typography = JetnewsTypography,
shapes = JetnewsShapes,
content = content)
}
下边是修改的地方,弄了两个button,点击修改主题
Column(Modifier
.padding(it)
.fillMaxSize(), verticalArrangement = Arrangement.Center) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
var theme by remember {
val theme = context.dataStore.data.map { preferences ->
preferences[themeKey] ?: 0
}
mutableStateOf(theme)
}
var mytheme = theme.collectAsState(initial = 0).value
Text(text = "current theme $mytheme")
Row {
RadioButton(selected = mytheme == 0, onClick = {
scope.launch {
context.dataStore.edit { preferences ->
preferences[themeKey] = 0
}
}
})
Text(text = "default theme 0")
}
Row {
RadioButton(selected = mytheme == 1, onClick = {
scope.launch {
context.dataStore.edit { preferences ->
preferences[themeKey] = 1
}
}
})
Text(text = "theme 1")
}
}
2. Proto DataStore
implementation("androidx.datastore:datastore:1.0.0")
2.1 数据的定义
首先需要在下边的目录 app/src/main/proto/ 下添加一个xxx.proto文件
proto语法 developers.google.com/protocol-bu…
android studio 可以安装一个插件,这样方便书写proto文件,你新建一个proto后缀的文件,会自动提示你要不要安装的,点安装就可以了.比如
syntax = "proto3";
option java_package = "com.example.application";
option java_multiple_files = true;
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3; }
当然了,这个proto文件需要编译的,这样才会生成message对应的java类
window系统下,用android studio
参考这里:这个比较早
或者参考 codelab,建议用这个
简单记录下需要添加的内容
plugins {
...
id "com.google.protobuf" version "0.8.17"
}
dependencies {
implementation "androidx.datastore:datastore:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.19.4"
...
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.19.4"
}
// 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'
}
}
}
}
// 默认生成目录 $buildDir/generated/source/proto 通过 generatedFilesBaseDir 改变生成位置
//generatedFilesBaseDir = "$projectDir/src/main"
}
sync一下,完事rebuild,就可以在build目录下看到生成的class类了,如下图
message对应的类已经生成了,下边就可以继续了
创建 Proto DataStore 来存储类型化对象涉及两个步骤
- 定义一个实现
Serializer<T>的类,其中T是 proto 文件中定义的类型。此序列化器类会告知 DataStore 如何读取和写入您的数据类型。请务必为该序列化器添加默认值,以便在尚未创建任何文件时使用。 - 使用由
dataStore创建的属性委托来创建DataStore<T>的实例,其中T是在 proto 文件中定义的类型。在您的 Kotlin 文件顶层调用该实例一次,便可在应用的所有其余部分通过此属性委托访问该实例。filename参数会告知 DataStore 使用哪个文件存储数据,而serializer参数会告知 DataStore 第 1 步中定义的序列化器类的名称
createDataStore 除了需要知道文件名字,还需要一个Serialize对象,其实自动生成的那几个java文件里把序列化反序列化都写好了,如下,直接调用方法即可parseFrom,writeTo
如下,先写个message类对应的序列化类
import androidx.datastore.core.Serializer
import com.example.jetnews.SearchRequest
import java.io.InputStream
import java.io.OutputStream
object SearchRequestSerialize :Serializer<SearchRequest>{
override val defaultValue: SearchRequest
get() = SearchRequest.getDefaultInstance()
override suspend fun readFrom(input: InputStream): SearchRequest {
return SearchRequest.parseFrom(input)
}
override suspend fun writeTo(t: SearchRequest, output: OutputStream) {
t.writeTo(output)
}
}
接着创建dataStore
val Context.requestDataStore: DataStore<SearchRequest> by dataStore(
fileName = "requestData.pb",
serializer = SearchRequestSerialize
)
现在就可以存取SearchRequest数据了,下边演示了如何存取
@Composable
fun testProtoData() {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val flowRequest by remember {
mutableStateOf(context.requestDataStore.data)
}
val request = flowRequest.collectAsState(initial = SearchRequest.getDefaultInstance()).value
Text(text = "request data: ${request}")
val page = request.pageNumber
Button(onClick = {
scope.launch {
context.requestDataStore.updateData { searchRequest ->
searchRequest.toBuilder().setPageNumber(page + 1).setQuery(page.toString()).setResultPerPage(page*2).build()
}
}
}) {
Text(text = "change request data,page +1")
}
}
end.