参考Android官方文档 使用 Jetpack DataStore 进行数据存储 以及 官网介绍 Android DataStore 写了一个demo实例。
一、Preferences DataStore
1.需要kotlin的开发环境,因为DataStore内部是用协程实现的
2.使用时需添加依赖
dependencies {
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
}
3.代码中使用,如下例:
package com.xxx.androidstudydemo.datastore
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.createDataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.preferencesKey
import com.hxy.androidstudydemo.DemoApp
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
object DataStoreHelper {
private val TAG = "DataStoreHelper"
//1.创建Preference DataStore
private val dataStore: DataStore<Preferences> = DemoApp.mContext.createDataStore(
name = "data_store_demo"
)
//2.定义键
private val TEST_KEY01 = preferencesKey<String>("test_key01")
private val TEST_KEY02 = preferencesKey<Int>("test_key02")
private val TEST_KEY03 = preferencesKey<Boolean>("test_key03")
//3.设置键值
suspend fun setTestKey01(value:String ) {
dataStore.edit { preferences -> preferences[TEST_KEY01] = value }
}
suspend fun setTestKey02(value:Int ) {
dataStore.edit { preferences -> preferences[TEST_KEY02] = value }
}
suspend fun setTestKey01(value:Boolean ) {
dataStore.edit { preferences -> preferences[TEST_KEY03] = value }
}
//4.获取值
suspend fun getTestKey01(): String {
return dataStore.data.map { preferences -> preferences[TEST_KEY01] }.first() ?: ""
}
suspend fun getTestKey02(): Int {
return dataStore.data.map { preferences -> preferences[TEST_KEY02] }.first() ?: 0
}
/*
错误写法,使用collect的话Flow一直不结束,所以协程一直挂起状态,得不到值
suspend fun getTestKey02(): Int {
var value: Int = 0
dataStore.data.map { preferences -> preferences[TEST_KEY02] }.collect { value = it?: 0 }
return value
}*/
suspend fun getTestKey03(): Boolean {
return dataStore.data.map { preferences -> preferences[TEST_KEY03] }.first() ?: false
}
fun testFun() = runBlocking {
Log.d(TAG, "字串初始值:" + getTestKey01())
setTestKey01("Hello world!")
Log.d(TAG, "字串终值:" + getTestKey01())
Log.d(TAG, "数初始值:" + getTestKey02())
setTestKey02(666)
Log.d(TAG, "数终值:" + getTestKey02())
Log.d(TAG, "布尔初始值:" + getTestKey03())
setTestKey01(true)
Log.d(TAG, "布尔终值:" + getTestKey03())
}
}
这里需要强调的一点就是在获取值的时候,因为是采用的Flow的机制实现的,所以如果采用collect的方式来获取值会出现Flow一直不取消,协程一直挂起等待的状态,得不到值。这里应该采用flow.first()的方式获取第一个值,然后从API介绍可知此方法会自动自动取消collect。
使用上跟SharedPreference很类似,也很简单,Android官方推荐使用,但目前还处于测试版本。
二、Proto DataStore
环境配置:基于AndroidStudio4.1.2
1.先AS中安装一个插件Protocol Buffer Editor,方便proto文件的编写和格式化
2.配置proto的编译环境:
a)工程目录build.gradle中添加插件
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.14'
}
b)模块目录build.gradle中加入如下内容:原有内容以“...”替代,对照层级加入相应配置
plugins {
.......
id 'com.google.protobuf'
}
android {
........
sourceSets {
main {
proto {
srcDir 'src/main/proto'
include '**/*.proto'
}
java {
srcDir 'src/main/java'
}
}
}
}
dependencies {
..........
implementation "androidx.datastore:datastore:1.0.0-alpha05"
implementation "com.google.protobuf:protobuf-javalite:3.14.0"
}
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'
}
}
}
}
}
3.编写proto文件,路径为上述配置中的src/main/proto下,这个可以修改,命名为xxx.proto,并重新rebuild project,其会在build->generated/source/下生成此proto相关的java代码
syntax = "proto3";
option java_package = "com.hxy.androidstudydemo.datastore";
option java_multiple_files = true;
message TestData {
int32 test_id = 1;
string msg = 2;
bool enable = 3;
}
4.将生成的与proto相匹配的java类序列化
object TestDataSerializer : Serializer<TestData> {//TestData即为编译生成的java类
override fun readFrom(input: InputStream): TestData {
return TestData.parseFrom(input)//序列化过程只需简单的对应一下
}
override fun writeTo(t: TestData, output: OutputStream) {
t.writeTo(output)
}
override val defaultValue: TestData
get() = TestData.getDefaultInstance()
}
5.使用4中的序列化文件创建Proto DataStore实例,如下代码;
class TestProtoDataStore {
private val dataStore by lazy {
DemoApp.mContext.createDataStore(
fileName = FILENAME,
serializer = TestDataSerializer//上一步序列化后的类
)
}
suspend fun setTestDataId(id: Int) {
dataStore.updateData {
it.toBuilder().setTestId(id).build()
}
}
suspend fun getTestDataId(): Int {
return dataStore.data.map { it.testId }.first()
}
suspend fun setTestDataMsg(msg: String) {
dataStore.updateData {
it.toBuilder().setMsg(msg).build()
}
}
suspend fun getTestDataMsg(): String {
return dataStore.data.map { it.msg }.first()
}
suspend fun setTestDataEnable(bool: Boolean) {
dataStore.updateData {
it.toBuilder().setEnable(bool).build()
}
}
suspend fun isTestDataEnable(): Boolean {
return dataStore.data.map { it.enable }.first()
}
suspend fun setTestData(data: TestData) {
dataStore.updateData {
it.toBuilder()
.setTestId(data.testId)
.setEnable(data.enable)
.setMsg(data.msg)
.build()
}
}
suspend fun getTestData(): TestData {
return dataStore.data.first()
}
companion object{
const val FILENAME = "test.pb"
fun testFun() = runBlocking {
val testData = TestData.getDefaultInstance()
}
}
}
接下来就可以像一般类和方法一样使用了。
注意:Preference DataStore和Proto DataStore引用的jar包有区别,如果工程中同时使用了二者,需要将两个包都加进去;请注意到:Preference DataStore创建时使用的是
androidx.datastore.preferences.PreferenceDataStore.Context.createDataStore(name="xxxx")
而Proto DataStore是androidx.datastore.DataStoreFactory.Context.createDataStore(fileName="xxx.pb",serializer = XXX)
源码:CODE.CHINA Android-studydemo
为什么使用DataStore?
官方给了一张图应该能介绍清楚了:
参考文档:
ProtoBuffer官网 https://developers.google.cn/protocol-buffers
官网介绍 https://developer.android.google.cn/topic/libraries/architecture/datastore#typed-datastore
Android Jetpack 之 DataStore https://blog.youkuaiyun.com/zzw0221/article/details/109274610
GitHub上DataStoreSample https://github.com/danledian/DataStoreSample